home *** CD-ROM | disk | FTP | other *** search
/ MacHack 2000 / MacHack 2000.toast / pc / The Hacks / Dialing Addresses / Src / Address.c next >
Encoding:
C/C++ Source or Header  |  2000-06-23  |  240.4 KB  |  8,532 lines

  1. /***********************************************************************
  2.  *
  3.  Copyright © 1995 - 1998, 3Com Corporation or its subsidiaries ("3Com").  
  4.  All rights reserved.
  5.    
  6.  This software may be copied and used solely for developing products for 
  7.  the Palm Computing platform and for archival and backup purposes.  Except 
  8.  for the foregoing, no part of this software may be reproduced or transmitted 
  9.  in any form or by any means or used to make any derivative work (such as 
  10.  translation, transformation or adaptation) without express written consent 
  11.  from 3Com.
  12.  
  13.  3Com reserves the right to revise this software and to make changes in content 
  14.  from time to time without obligation on the part of 3Com to provide notification 
  15.  of such revision or changes.  
  16.  3COM MAKES NO REPRESENTATIONS OR WARRANTIES THAT THE SOFTWARE IS FREE OF ERRORS 
  17.  OR THAT THE SOFTWARE IS SUITABLE FOR YOUR USE.  THE SOFTWARE IS PROVIDED ON AN 
  18.  "AS IS" BASIS.  3COM MAKES NO WARRANTIES, TERMS OR CONDITIONS, EXPRESS OR IMPLIED, 
  19.  EITHER IN FACT OR BY OPERATION OF LAW, STATUTORY OR OTHERWISE, INCLUDING WARRANTIES, 
  20.  TERMS, OR CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND 
  21.  SATISFACTORY QUALITY.
  22.  
  23.  TO THE FULL EXTENT ALLOWED BY LAW, 3COM ALSO EXCLUDES FOR ITSELF AND ITS SUPPLIERS 
  24.  ANY LIABILITY, WHETHER BASED IN CONTRACT OR TORT (INCLUDING NEGLIGENCE), FOR 
  25.  DIRECT, INCIDENTAL, CONSEQUENTIAL, INDIRECT, SPECIAL, OR PUNITIVE DAMAGES OF 
  26.  ANY KIND, OR FOR LOSS OF REVENUE OR PROFITS, LOSS OF BUSINESS, LOSS OF INFORMATION 
  27.  OR DATA, OR OTHER FINANCIAL LOSS ARISING OUT OF OR IN CONNECTION WITH THIS SOFTWARE, 
  28.  EVEN IF 3COM HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
  29.  
  30.  3Com, HotSync, Palm Computing, and Graffiti are registered trademarks, and 
  31.  Palm III and Palm OS are trademarks of 3Com Corporation or its subsidiaries.
  32.  
  33.  IF THIS SOFTWARE IS PROVIDED ON A COMPACT DISK, THE OTHER SOFTWARE AND 
  34.  DOCUMENTATION ON THE COMPACT DISK ARE SUBJECT TO THE LICENSE AGREEMENT 
  35.  ACCOMPANYING THE COMPACT DISK.
  36.  
  37.  ************************************************************************
  38.  *
  39.  * PROJECT:  Pilot
  40.  * FILE:     address.c
  41.  * AUTHOR:    Art Lamb: Feb 13, 1995
  42.  *
  43.  * DECLARER: Address
  44.  *
  45.  * DESCRIPTION:
  46.  *     This is the Address Book application's main module.  This module
  47.  *   starts the application, dispatches events, and stops
  48.  *   the application. 
  49.  *
  50.  **********************************************************************/
  51.  
  52. #include <Pilot.h>
  53. #include <CharAttr.h>
  54. #include <Graffiti.h>
  55. #include <Keyboard.h>
  56. #include <SysEvtMgr.h>
  57. #include <KeyMgr.h>
  58.  
  59. #define   NON_PORTABLE
  60. #include <SystemPrv.h>
  61.  
  62. #include "Address.h"
  63. #include "AddressRsc.h"
  64.  
  65. typedef    unsigned char UInt8;
  66. #include "AudioLib.h"
  67.  
  68. /***********************************************************************
  69.  *
  70.  *   Entry Points
  71.  *
  72.  ***********************************************************************/
  73. DWord   PilotMain (Word cmd, Ptr cmdPBP, Word launchFlags);
  74. static Err RomVersionCompatible (DWord requiredVersion, Word launchFlags);
  75.  
  76. /***********************************************************************
  77.  *
  78.  *   Internal Structures
  79.  *
  80.  ***********************************************************************/
  81.  
  82. typedef struct {
  83.     Word                currentCategory;
  84.     FontID            v20NoteFont;                // For 2.0 compatibility (BGT)
  85.     Boolean            showAllCategories;
  86.     Boolean            saveBackup;
  87.     Boolean            rememberLastCategory;
  88.     
  89.     // Version 3 preferences
  90.     FontID            addrListFont;
  91.     FontID            addrRecordFont;
  92.     FontID            addrEditFont;
  93.     DWord            businessCardRecordID;
  94.     FontID            noteFont;
  95. } AddrPreferenceType;
  96.  
  97.  
  98. // Info on how to draw the record view
  99. typedef struct {
  100.     UInt                fieldNum;
  101.     UInt                length;
  102.     UInt                offset;
  103.     UInt                x;
  104. } RecordViewLineType;
  105.  
  106.  
  107.  
  108.  
  109. /***********************************************************************
  110.  *
  111.  *   Global variables
  112.  *
  113.  ***********************************************************************/
  114.  
  115. static DmOpenRef       AddrDB;
  116. static char            CategoryName [dmCategoryLength];
  117. static Word            AddressInCategory;
  118. static Boolean         HideSecretRecords;
  119. static Word            TopVisibleRecord = 0;
  120. static Word            CurrentRecord = noRecord;
  121. static Word            CurrentView = ListView;
  122. static Word            ListViewSelectThisRecord = noRecord;        // This must
  123.                                                        // be set whenever we leave a
  124.                                                     // dialog because a frmSaveEvent
  125.                                                     // happens whenever the focus is
  126.                                                     // lost in the EditView and then
  127.                                                     // a find and goto can happen
  128.                                                     // causing a wrong selection to
  129.                                                     // be used.
  130. Boolean                        SortByCompany;
  131. const  Int                    PhoneColumnWidth = 82; // (415)-000-0000x...
  132. static AddrDBRecordType recordViewRecord;
  133. static Handle                recordViewRecordH = 0;
  134. static RecordViewLineType *RecordViewLines;
  135. static UInt                    RecordViewLastLine;   // Line after last one containing data
  136. static UInt                    TopRecordViewLine;
  137. static UInt                    RecordViewFirstPlainLine;
  138. static Word                    TopVisibleFieldIndex;
  139. static Word                    CurrentFieldIndex;
  140. static Word                    EditRowIDWhichHadFocus;
  141. static Word                    EditFieldPosition;
  142. static Word                    PriorAddressFormID;   // Used for NoteView
  143. static Word                    EditLabelColumnWidth = 0;
  144. static Word                    RecordLabelColumnWidth = 0;
  145. static CharPtr                UnnamedRecordStringPtr = 0;
  146. static Boolean                RecordNeededAfterEditView;
  147. static DWord                TickAppButtonPushed = 0;
  148. static Word                    AppButtonPushed = nullChr;
  149. static Word                    AppButtonPushedModifiers = 0;
  150. static Boolean                BusinessCardSentForThisButtonPress = false;
  151.  
  152. // The following global variable are saved to a state file.
  153. static Word                    CurrentCategory = dmAllCategories;
  154. static Boolean                ShowAllCategories = true;
  155. static Boolean                SaveBackup = true;
  156. static Boolean                RememberLastCategory = false;
  157. static FontID                NoteFont = stdFont;
  158. static FontID                AddrListFont = stdFont;
  159. static FontID                AddrRecordFont = largeBoldFont;
  160. static FontID                AddrEditFont = largeBoldFont;
  161. static DWord                BusinessCardRecordID = dmUnusedRecordID;
  162.  
  163. // These are used for accelerated scrolling
  164. static UInt                 LastSeconds = 0;
  165. static UInt                 ScrollUnits = 0;
  166.  
  167. // The following structure maps row in the edit table to fields in the
  168. // address record.  This controls the order in which fields are edited.
  169. static AddressFields    FieldMap [] = {
  170.     name,
  171.     firstName,
  172.     title,
  173.     company,
  174.     phone1,
  175.     phone2,
  176.     phone3,
  177.     phone4,
  178.     phone5,
  179.     address,
  180. #if COUNTRY == COUNTRY_UNITED_STATES
  181.     city,
  182.     state,
  183.     zipCode,
  184. #else      // COUNTRY_FRANCE or COUNTRY_UNITED_STATES
  185.     zipCode,
  186.     city,
  187.     state,
  188. #endif
  189.     country,
  190.     custom1,
  191.     custom2,
  192.     custom3,
  193.     custom4,
  194.     note
  195.    };
  196.  
  197. // Valid after StartApplication
  198. static char PhoneLabelLetters[numPhoneLabels];
  199.  
  200. // Valid after EditViewInit
  201. static CharPtr EditPhoneListChoices[numPhoneLabels];
  202.  
  203. // Valid after DetailsDialogInit
  204. static CharPtr DetailsPhoneListChoices[numPhoneFields];
  205.  
  206.  
  207.  
  208.  
  209. /***********************************************************************
  210.  *
  211.  *    Internal Constants
  212.  *
  213.  ***********************************************************************/
  214. #define maxNoteTitleLen                   40
  215.  
  216. // Address list table columns
  217. #define nameAndNumColumn                   0
  218. #define noteColumn                         1
  219.  
  220. // Address edit table's rows and columns
  221. #define editLabelColumn                   0
  222. #define editDataColumn                    1
  223.  
  224. #define spaceBeforeDesc                   2
  225.  
  226. #define editLastFieldIndex                17
  227. #define editFirstFieldIndex                0
  228.  
  229. // Update codes, used to determine how the address list view should 
  230. // be redrawn.
  231. #define updateRedrawAll                   0x01
  232. #define updateGrabFocus                            0x02
  233. #define updateItemHide                            0x04
  234. #define updateCategoryChanged                    0x08
  235. #define updateFontChanged                        0x10
  236. #define updateListViewPhoneChanged        0x20
  237. #define updateCustomFieldLabelChanged     0x40
  238. #define updateSelectCurrentRecord          0x80
  239.  
  240. #define addrEditLabelFont                        stdFont
  241. #define addrEditBlankFont                        stdFont
  242.  
  243. // number of record view lines to store
  244. #define recordViewLinesMax                55
  245. #define recordViewBlankLine                0xffff   // Half height if the next line.x == 0
  246.  
  247.  
  248. // Scroll rate values
  249. #define scrollDelay                2
  250. #define scrollAcceleration        2
  251. #define scrollSpeedLimit        5
  252.  
  253.  
  254. #define noFieldIndex            0xff
  255.  
  256. // Time to depress the app's button to send a business card
  257. #define AppButtonPushTimeout                    (sysTicksPerSecond)
  258.  
  259. // Maximum label column width in Edit and Record views.
  260. //  (Would be nice if this was based on window size or screen size, do 1/2 screen for now)
  261. #define maxLabelColumnWidth        80    
  262.  
  263.  
  264. /***********************************************************************
  265.  *
  266.  *    Internal Functions
  267.  *
  268.  ***********************************************************************/
  269. #define isPhoneField(f)      (f >= firstPhoneField && f <= lastPhoneField)
  270.  
  271. static void DrawRecordNameAndPhoneNumber (AddrDBRecordPtr record, 
  272.     RectanglePtr bounds, CharPtr phoneLabelLetters, Boolean sortByCompany,
  273.     CharPtr *unnamedRecordStringPtr);
  274.  
  275. static void DeleteNote (void);
  276. static void ListViewSelectRecord (UInt recordNum);
  277. static void ListViewScroll (DirectionType direction, UInt units, Boolean byLine);
  278.  
  279. static void EditViewLoadTable (void);
  280. static void EditViewInit (FormPtr frm, Boolean leaveAppInfoUnlocked);
  281.  
  282. static Boolean AddrSendBusinessCard (DmOpenRef dbP);
  283.  
  284. static DWord AddrPilotMain (Word cmd, Ptr cmdPBP, Word launchFlags);
  285.  
  286. // Scroll rate prototypes
  287. static void ResetScrollRate(void);
  288. static void AdjustScrollRate(void);
  289. static void AddressLoadPrefs(AddrAppInfoPtr    appInfoPtr);    // (BGT)
  290. static void AddressSavePrefs(void);                            // (BGT)
  291.  
  292. /***********************************************************************
  293.  *
  294.  * FUNCTION:     StartApplication
  295.  *
  296.  * DESCRIPTION:  This routine opens the application's resource file and
  297.  *               database.
  298.  *
  299.  * PARAMETERS:   nothing
  300.  *
  301.  * RETURNED:     nothing
  302.  *
  303.  * REVISION HISTORY:
  304.  *         Name   Date      Description
  305.  *         ----   ----      -----------
  306.  *         art   6/5/95   Initial Revision
  307.  *         vmk   12/12/97 Get amplitude for scroll sound
  308.  *           BGT     1/8/98      Use AddressLoadPrefs to load and fix up the
  309.  *                          application preferences
  310.  *
  311.  ***********************************************************************/
  312. static Err StartApplication(void)
  313. {
  314.     Err err = 0;
  315.     UInt mode;
  316.     SystemPreferencesType sysPrefs;
  317.     AddrAppInfoPtr appInfoPtr;
  318.     UInt cardNo;
  319.     LocalID dbID;
  320.     Word attributes;
  321.  
  322.    
  323.    // Determime if secert record should be shown.
  324.     PrefGetPreferences (&sysPrefs);
  325.     HideSecretRecords = sysPrefs.hideSecretRecords;
  326.     if (sysPrefs.hideSecretRecords)
  327.        mode = dmModeReadWrite;
  328.     else
  329.        mode = dmModeReadWrite | dmModeShowSecret;
  330.  
  331.  
  332.    // Find the application's data file.  If it doesn't exist create it.
  333.     AddrDB = DmOpenDatabaseByTypeCreator (addrDBType, sysFileCAddress, mode);
  334.     if (!AddrDB)
  335.        {
  336.        err = DmCreateDatabase (0, addrDBName, sysFileCAddress, addrDBType, false);
  337.        if (err) 
  338.           return err;
  339.       
  340.        AddrDB = DmOpenDatabaseByTypeCreator (addrDBType, sysFileCAddress, mode);
  341.        if (! AddrDB) 
  342.           return (1);
  343.       
  344.         // Set the backup bit.  This is to aid syncs with non Palm software.
  345.         DmOpenDatabaseInfo(AddrDB, &dbID, NULL, NULL, &cardNo, NULL);
  346.         DmDatabaseInfo(cardNo, dbID, NULL, &attributes, NULL, NULL,
  347.                     NULL, NULL, NULL, NULL, NULL, NULL, NULL);
  348.         attributes |= dmHdrAttrBackup;
  349.         DmSetDatabaseInfo(cardNo, dbID, NULL, &attributes, NULL, NULL,
  350.                     NULL, NULL, NULL, NULL, NULL, NULL, NULL);
  351.         
  352.        err = AddrAppInfoInit (AddrDB);
  353.        if (err) 
  354.           {
  355.           DmCloseDatabase(AddrDB);
  356.           DmDeleteDatabase(cardNo, dbID);
  357.           return err;
  358.             }
  359.         }
  360.    
  361.  
  362.     appInfoPtr = (AddrAppInfoPtr) AddrAppInfoGetPtr(AddrDB);
  363.     ErrFatalDisplayIf(appInfoPtr == NULL, "Missing app info block");
  364.    
  365.    // Update the database to look and behave properly for the given country.
  366.     if (appInfoPtr->country != sysPrefs.country)
  367.        AddrChangeCountry(appInfoPtr);
  368.  
  369.  
  370.     InitPhoneLabelLetters(appInfoPtr, PhoneLabelLetters);
  371.       
  372.     SortByCompany = appInfoPtr->misc.sortByCompany;
  373.     
  374.     // Load the application preferences and fix them up if need be.    (BGT)
  375.     AddressLoadPrefs(appInfoPtr);                            // (BGT)
  376.  
  377.     // Start watching the button pressed to get into this app.  If it's held down
  378.     // long enough then we need to send the business card.
  379.     TickAppButtonPushed = TimGetTicks();
  380.     
  381.     // Mask off the key to avoid repeat keys causing clicking sounds
  382.     KeySetMask(~KeyCurrentState());
  383.     
  384.     return (err);
  385. }
  386.  
  387.  
  388. /***********************************************************************
  389.  *
  390.  * FUNCTION:    StopApplication
  391.  *
  392.  * DESCRIPTION: This routine close the application's database
  393.  *              and save the current state of the application.
  394.  *
  395.  * PARAMETERS:  nothing
  396.  *
  397.  * RETURNED:    nothing
  398.  *
  399.  * REVISION HISTORY:
  400.  *         Name   Date      Description
  401.  *         ----   ----      -----------
  402.  *         art   6/5/95      Initial Revision
  403.  *
  404.  ***********************************************************************/
  405. static void StopApplication(void)
  406. {
  407.     // Write the preferences / saved-state information.
  408.     AddressSavePrefs();
  409.  
  410.     // Send a frmSave event to all the open forms.
  411.     FrmSaveAllForms ();
  412.  
  413.     // Close all the open forms.
  414.     FrmCloseAllForms ();
  415.  
  416.     // Close the application's data file.
  417.     DmCloseDatabase (AddrDB);
  418.  
  419. }
  420.  
  421.  
  422. /***********************************************************************
  423.  *
  424.  * FUNCTION:    AppHandleSync
  425.  *
  426.  * DESCRIPTION: Handle details after the database has been synchronized.
  427.  * This app resorts the database. 
  428.  *
  429.  * PARAMETERS:    findParams
  430.  *
  431.  * RETURNED:    nothing
  432.  *
  433.  * REVISION HISTORY:
  434.  *            Name   Date            Description
  435.  *            ----   ----            -----------
  436.  *            roger    8/31/95        Initial Revision
  437.  *            vmk    10/17/95        Changed to open db read/write
  438.  *
  439.  ***********************************************************************/
  440. static void AppHandleSync(void)
  441. {
  442.     DmOpenRef dbP;
  443.     AddrAppInfoPtr appInfoPtr;
  444.     UInt mode;
  445. //   char name [dmCategoryLength];
  446. //   AddrPreferenceType prefs;
  447.  
  448.  
  449.    // Find the application's data file.
  450.     mode = dmModeReadWrite;
  451.     dbP = DmOpenDatabaseByTypeCreator(addrDBType, sysFileCAddress, mode);
  452.     if (!dbP) 
  453.       return;
  454.    
  455.    
  456.    // If we have state information insure it's still valid.
  457. /*   if ( PrefGetAppPreferences (sysFileCAddress, addrVersionNum, &prefs, 
  458.       sizeof (AddrPreferenceType))){
  459.  
  460.       // Check if the currrent category still exist.
  461.       CategoryGetName (dbP, prefs.currentCategory, name);   
  462.       if (*name == 0){
  463.          prefs.currentCategory = dmAllCategories;
  464.          prefs.showAllCategories = true;
  465.    
  466.          PrefSetAppPreferences (sysFileCAddress, addrVersionNum, &prefs, 
  467.             sizeof (AddrPreferenceType));
  468.       }
  469.    }
  470. */
  471.  
  472.     appInfoPtr = (AddrAppInfoPtr) AddrAppInfoGetPtr(dbP);
  473.    
  474.    AddrChangeSortOrder(dbP, appInfoPtr->misc.sortByCompany);
  475.  
  476.    MemPtrUnlock(appInfoPtr);
  477.  
  478.    DmCloseDatabase (dbP);   
  479. }
  480.  
  481.  
  482. /***********************************************************************
  483.  *
  484.  * FUNCTION:    AppLaunchCmdDatabaseInit
  485.  *
  486.  * DESCRIPTION: Initialize an empty database.
  487.  *
  488.  * PARAMETERS:    dbP - pointer to database opened for read & write
  489.  *
  490.  * RETURNED:    true if successful
  491.  *
  492.  * REVISION HISTORY:
  493.  *         Name   Date      Description
  494.  *         ----   ----      -----------
  495.  *         roger   10/19/95   Initial Revision
  496.  *
  497.  ***********************************************************************/
  498. static Boolean AppLaunchCmdDatabaseInit(DmOpenRef dbP)
  499. {
  500.    Err err;
  501.    if (!dbP) 
  502.       return false;
  503.  
  504.    // Initialize the database's app info block
  505.    
  506.    err = AddrAppInfoInit (dbP);
  507.    if (err) 
  508.       return false;
  509.    return true;
  510. }
  511.  
  512.  
  513. /***********************************************************************
  514.  *
  515.  * FUNCTION:    AppValidateDataFiles
  516.  *
  517.  * DESCRIPTION: Validate all of the application's data files for
  518.  * integrity.  Useful after a crash has occured. 
  519.  *
  520.  * Currently unused.
  521.  *
  522.  * PARAMETERS:    nothing
  523.  *
  524.  * RETURNED:    nothing
  525.  *
  526.  * REVISION HISTORY:
  527.  *         Name   Date      Description
  528.  *         ----   ----      -----------
  529.  *         roger   9/1/95   Initial Revision
  530.  *
  531.  ***********************************************************************/
  532. static void AppValidateDataFiles ()
  533. {
  534.    LocalID dbID;
  535.    DmOpenRef dbP;
  536.    AddrAppInfoPtr appInfoPtr;
  537.    UInt      cardNo=0;
  538. /*   AddrDBRecordType record;
  539.    Word i;
  540.    Word pos;
  541.    CharPtr header;
  542.    UInt recordNum;
  543.    VoidHand recordH;
  544.    RectangleType r;
  545.    Boolean done;
  546.    char searchPhoneLabelLetters[numPhoneLabels];
  547. */
  548.  
  549.    // Open the record database.
  550.    dbID = DmFindDatabase (cardNo, addrDBName);
  551.    if (! dbID)
  552.       return;
  553.       
  554.    dbP = DmOpenDatabase(cardNo, dbID, dmModeReadWrite | dmModeExclusive);
  555.    if (! dbP)
  556.       return;
  557.  
  558.    appInfoPtr = (AddrAppInfoPtr) AddrAppInfoGetPtr(dbP);
  559.    
  560.    AddrChangeSortOrder(dbP, appInfoPtr->misc.sortByCompany);
  561.  
  562.    MemPtrUnlock(appInfoPtr);
  563.    DmCloseDatabase (dbP);   
  564. }
  565.  
  566.  
  567. /***********************************************************************
  568.  *
  569.  * FUNCTION:    Search
  570.  *
  571.  * DESCRIPTION: This routine searchs the the address database for records 
  572.  *              contains the string passed. 
  573.  *
  574.  * PARAMETERS:    findParams
  575.  *
  576.  * RETURNED:    nothing
  577.  *
  578.  * REVISION HISTORY:
  579.  *         Name   Date      Description
  580.  *         ----   ----      -----------
  581.  *         art   6/5/95   Initial Revision
  582.  *
  583.  ***********************************************************************/
  584. static void Search(FindParamsPtr params)
  585. {
  586.    AddrAppInfoPtr       appInfoPtr;
  587.    AddrDBRecordType     record;
  588.    Boolean              done;
  589.    Boolean              match;
  590.    char                 searchPhoneLabelLetters[numPhoneLabels];
  591.    CharPtr              header;
  592.    CharPtr              unnamedRecordStringPtr = NULL;
  593.    DmOpenRef            dbP;
  594.    DmSearchStateType    searchState;
  595.    Err                  err;
  596.    Handle               headerStringH;
  597.    LocalID              dbID;
  598.    RectangleType        r;
  599.    UInt                 cardNo=0;
  600.    UInt                 recordNum;
  601.    VoidHand             recordH;
  602.    Word                 i;
  603.    Word                 pos;
  604.  
  605.  
  606.    // Find the application's data file.
  607.    err = DmGetNextDatabaseByTypeCreator (true, &searchState, addrDBType,
  608.                sysFileCAddress, true, &cardNo, &dbID);
  609.    if (err)
  610.        {
  611.       params->more = false;
  612.       return;
  613.       }
  614.  
  615.    // Open the address database.
  616.    dbP = DmOpenDatabase(cardNo, dbID, params->dbAccesMode);
  617.    if (! dbP)
  618.        {
  619.       params->more = false;
  620.       return;
  621.        }
  622.  
  623.    // Display the heading line.
  624.    headerStringH = DmGetResource(strRsc, FindAddrHeaderStr);
  625.    header = MemHandleLock(headerStringH);
  626.    done = FindDrawHeader (params, header);
  627.    MemHandleUnlock(headerStringH);   
  628.    DmReleaseResource(headerStringH);   
  629.    if (done) 
  630.       goto Exit;
  631.  
  632.    // Search the description and note fields for the "find" string.
  633.    recordNum = params->recordNum;
  634.    while (true)
  635.        {
  636.       // Because applications can take a long time to finish a find when
  637.       // the result may be on the screen or for other reasons, users like
  638.       // to be able to stop the find.  Stop the find if an event is pending.
  639.       // This stops if the user does something with the device.  Because
  640.       // this call slows down the search we perform it every so many 
  641.       // records instead of every record.  The response time should still
  642.       // be short without introducing much extra work to the search.
  643.       
  644.       // Note that in the implementation below, if the next 16th record is  
  645.       // secret the check doesn't happen.  Generally this shouldn't be a 
  646.       // problem since if most of the records are secret then the search 
  647.       // won't take long anyways!
  648.       if ((recordNum & 0x000f) == 0 &&         // every 16th record
  649.          EvtSysEventAvail(true))
  650.          {
  651.          // Stop the search process.
  652.          params->more = true;
  653.          break;
  654.           }
  655.       
  656.       
  657.       recordH = DmQueryNextInCategory (dbP, &recordNum, dmAllCategories);
  658.  
  659.       // Have we run out of records?
  660.       if (! recordH)
  661.           {
  662.          params->more = false;         
  663.          break;
  664.           }
  665.  
  666.       // Search all the fields of the address record.
  667.       AddrGetRecord (dbP, recordNum, &record, &recordH);
  668.       match = false;
  669.       for (i = 0; i < addrNumFields; i++)
  670.           {
  671.          if (record.fields[i])
  672.              {
  673.             match = FindStrInStr (record.fields[i], params->strToFind, &pos);
  674.             if (match) 
  675.                break;
  676.              }
  677.           }
  678.       
  679.       if (match)
  680.           {
  681.          done = FindSaveMatch (params, recordNum, pos, i, 0, cardNo, dbID);
  682.          if (done)
  683.              {
  684.             MemHandleUnlock(recordH);
  685.             break;
  686.              }
  687.  
  688.          // Get the bounds of the region where we will draw the results.
  689.          FindGetLineBounds (params, &r);
  690.          
  691.  
  692.          appInfoPtr = (AddrAppInfoPtr) AddrAppInfoGetPtr(dbP);
  693.          InitPhoneLabelLetters(appInfoPtr, searchPhoneLabelLetters);
  694.  
  695.          // Display the title of the description.
  696.             FntSetFont (stdFont);
  697.          DrawRecordNameAndPhoneNumber (&record, &r, searchPhoneLabelLetters,
  698.             appInfoPtr->misc.sortByCompany, &unnamedRecordStringPtr);
  699.          
  700.          MemPtrUnlock(appInfoPtr);
  701.  
  702.          params->lineNumber++;
  703.           }
  704.  
  705.       MemHandleUnlock(recordH);
  706.       recordNum++;
  707.    }
  708.    
  709.    if (unnamedRecordStringPtr)
  710.        {
  711.       MemPtrUnlock(unnamedRecordStringPtr);
  712. //      DmReleaseResource(strRsc, UnnamedRecordStr)
  713.        }
  714.    
  715. Exit:
  716.    DmCloseDatabase (dbP);   
  717. }
  718.  
  719.  
  720. /***********************************************************************
  721.  *
  722.  * FUNCTION:    GoToItem
  723.  *
  724.  * DESCRIPTION: This routine is a entry point of this application.
  725.  *              It is generally call as the result of hiting of 
  726.  *              "Go to" button in the text search dialog.
  727.  *
  728.  * PARAMETERS:    recordNum - 
  729.  *
  730.  * RETURNED:    nothing
  731.  *
  732.  * REVISION HISTORY:
  733.  *         Name   Date      Description
  734.  *         ----   ----      -----------
  735.  *         roger   7/12/95   Initial Revision
  736.  *
  737.  ***********************************************************************/
  738. static void GoToItem (GoToParamsPtr goToParams, Boolean launchingApp)
  739. {
  740.    Word formID;
  741.    Word recordNum;
  742.    UInt attr;
  743.    ULong uniqueID;
  744.    EventType event;
  745.  
  746.  
  747.    recordNum = goToParams->recordNum;
  748.    DmRecordInfo (AddrDB, recordNum, &attr, &uniqueID, NULL);
  749.  
  750.    // Change the current category if necessary.
  751.    if (CurrentCategory != dmAllCategories)
  752.        {
  753.       CurrentCategory = attr & dmRecAttrCategoryMask;
  754.        }
  755.  
  756.    
  757.    // If the application is already running, close all the open forms.  If
  758.    // the current record is blank, then it will be deleted, so we'll 
  759.    // the record's unique id to find the record index again, after all 
  760.    // the forms are closed.
  761.    if (! launchingApp)
  762.        {
  763.       FrmCloseAllForms ();
  764.       DmFindRecordByID (AddrDB, uniqueID, &recordNum);
  765.        }
  766.       
  767.  
  768.    // Set global variables that keep track of the currently record.
  769.    CurrentRecord = recordNum;
  770.    
  771.    // Set PriorAddressFormID so the Note View returns to the List View
  772.    PriorAddressFormID = ListView;
  773.  
  774.  
  775.    if (goToParams->matchFieldNum == note)
  776.       formID = NoteView;
  777.    else
  778.       formID = RecordView;
  779.  
  780.    MemSet (&event, sizeof(EventType), 0);
  781.  
  782.    // Send an event to load the form we want to goto.
  783.    event.eType = frmLoadEvent;
  784.    event.data.frmLoad.formID = formID;
  785.    EvtAddEventToQueue (&event);
  786.  
  787.    // Send an event to goto a form and select the matching text.
  788.    event.eType = frmGotoEvent;
  789.    event.data.frmGoto.formID = formID;
  790.    event.data.frmGoto.recordNum = recordNum;
  791.    event.data.frmGoto.matchPos = goToParams->matchPos;
  792.    event.data.frmGoto.matchLen = goToParams->searchStrLen;
  793.    event.data.frmGoto.matchFieldNum = goToParams->matchFieldNum;
  794.    EvtAddEventToQueue (&event);
  795.  
  796. }
  797.  
  798.  
  799. /***********************************************************************
  800.  *
  801.  * FUNCTION:    GetFocusObjectPtr
  802.  *
  803.  * DESCRIPTION: This routine returns a pointer to the field object, in 
  804.  *              the current form, that has the focus.
  805.  *
  806.  * PARAMETERS:  nothing
  807.  *
  808.  * RETURNED:    pointer to a field object or NULL of there is no fucus
  809.  *
  810.  * REVISION HISTORY:
  811.  *         Name   Date      Description
  812.  *         ----   ----      -----------
  813.  *         art   2/21/95      Initial Revision
  814.  *
  815.  ***********************************************************************/
  816. static FieldPtr GetFocusObjectPtr (void)
  817. {
  818.    FormPtr frm;
  819.    Word focus;
  820.    
  821.    frm = FrmGetActiveForm ();
  822.    focus = FrmGetFocus (frm);
  823.    if (focus == noFocus)
  824.       return (NULL);
  825.       
  826.    return (FrmGetObjectPtr (frm, focus));
  827. }
  828.  
  829.  
  830. #pragma mark ----------------
  831. /***********************************************************************
  832.  *
  833.  * FUNCTION:    ResetScrollRate
  834.  *
  835.  * DESCRIPTION: This routine resets the scroll rate
  836.  *
  837.  * PARAMETERS:  nothing
  838.  *
  839.  * RETURNED:    nothing
  840.  *
  841.  * REVISION HISTORY:
  842.  *            Name        Date        Description
  843.  *            ----        ----        -----------
  844.  *            frigino    8/14/97    Initial Revision
  845.  *
  846.  ***********************************************************************/
  847. static void ResetScrollRate(void)
  848. {
  849.     // Reset last seconds
  850.     LastSeconds = TimGetSeconds();
  851.     // Reset scroll units
  852.     ScrollUnits = 1;
  853. }
  854.  
  855. /***********************************************************************
  856.  *
  857.  * FUNCTION:    AdjustScrollRate
  858.  *
  859.  * DESCRIPTION: This routine adjusts the scroll rate based on the current
  860.  *              scroll rate, given a certain delay, and plays a sound
  861.  *              to notify the user of the change
  862.  *
  863.  * PARAMETERS:  nothing
  864.  *
  865.  * RETURNED:    nothing
  866.  *
  867.  * REVISION HISTORY:
  868.  *            Name        Date        Description
  869.  *            ----        ----        -----------
  870.  *            frigino        8/14/97        Initial Revision
  871.  *            vmk            12/2/97        Fix crash from uninitialized sndCmd and
  872.  *                                    derive sound amplitude from system amplitude
  873.  *
  874.  ***********************************************************************/
  875. static void AdjustScrollRate(void)
  876. {
  877.     // Accelerate the scroll rate every 3 seconds if not already at max scroll speed
  878.     UInt newSeconds = TimGetSeconds();
  879.     if ((ScrollUnits < scrollSpeedLimit) && ((newSeconds - LastSeconds) > scrollDelay))
  880.         {
  881.         // Save new seconds
  882.         LastSeconds = newSeconds;
  883.         
  884.         // increase scroll units
  885.         ScrollUnits += scrollAcceleration;
  886.         }
  887.  
  888. }
  889.  
  890.  
  891. #pragma mark ----------------
  892. /***********************************************************************
  893.  *
  894.  * FUNCTION:    GetLabelColumnWidth
  895.  *
  896.  * DESCRIPTION: Calculate the width of the widest field label plus a ':'.  
  897.  *
  898.  * PARAMETERS:  appInfoPtr  - pointer to the app info block for field labels
  899.  *              labelFontID - font 
  900.  *
  901.  * RETURNED:    width of the widest label.
  902.  *
  903.  * REVISION HISTORY:
  904.  *         Name   Date      Description
  905.  *         ----   ----      -----------
  906.  *         art   1/30/98   Initial Revision
  907.  *
  908.  ***********************************************************************/
  909. static Word GetLabelColumnWidth (AddrAppInfoPtr appInfoPtr, FontID labelFontID)
  910. {
  911.     Int        i;
  912.     Word    labelWidth;     // Width of a field label
  913.     Word    columnWidth;    // Width of the label column (fits all label)
  914.     FontID    curFont;
  915.     CharPtr    label;
  916.  
  917.  
  918.     // Calculate column width of the label column which is used by the Record View and the
  919.     // Edit View.
  920.     curFont = FntSetFont (labelFontID);
  921.  
  922.     columnWidth = 0;
  923.  
  924.     for (i = firstAddressField; i < lastLabel; i ++)
  925.         {
  926.         label = appInfoPtr->fieldLabels[i];
  927.         labelWidth = FntCharsWidth(label, StrLen(label));
  928.         columnWidth = max(columnWidth, labelWidth);
  929.         }
  930.     columnWidth += 1 + FntCharWidth(':');
  931.  
  932.     FntSetFont (curFont);
  933.     
  934.     if (columnWidth > maxLabelColumnWidth)
  935.         columnWidth = maxLabelColumnWidth;
  936.  
  937.     return columnWidth;
  938. }
  939.  
  940.  
  941. /***********************************************************************
  942.  *
  943.  * FUNCTION:    LeaveForm
  944.  *
  945.  * DESCRIPTION: Leaves the current popup form and returns to the prior one.
  946.  *
  947.  * PARAMETERS:  formID  - resource id of form to return to 
  948.  *
  949.  * RETURNED:    nothing
  950.  *
  951.  * REVISION HISTORY:
  952.  *         Name   Date      Description
  953.  *         ----   ----      -----------
  954.  *         roger   6/30/95   Initial Revision
  955.  *
  956.  ***********************************************************************/
  957. static void LeaveForm  ()
  958. {
  959.    FormPtr frm;
  960.  
  961.    frm = FrmGetActiveForm();
  962.    FrmEraseForm (frm);
  963.    FrmDeleteForm (frm);
  964.    FrmSetActiveForm (FrmGetFirstForm ());
  965. }
  966.  
  967.  
  968.  
  969. /***********************************************************************
  970.  *
  971.  * FUNCTION:    WinDrawString
  972.  *
  973.  * DESCRIPTION: This function draws the string in the draw window.
  974.  *
  975.  * PARAMETERS:  string  pointer to the string to draw
  976.  *              x      left bound of first character to draw
  977.  *              y      right bound of first character to draw
  978.  *
  979.  * RETURNED:    nothing
  980.  *
  981.  * REVISION HISTORY:
  982.  *         Name   Date      Description
  983.  *         ----   ----      -----------
  984.  *         roger   6/22/95   Initial Revision
  985.  *
  986.  ***********************************************************************/
  987. static void WinDrawString (CharPtr string, short x, short y)
  988. {
  989.    WinDrawChars(string, StrLen(string), x, y);
  990. }
  991.  
  992.  
  993. /***********************************************************************
  994.  *
  995.  * FUNCTION:    ChangeCategory
  996.  *
  997.  * DESCRIPTION: This routine updates the global varibles that keep track
  998.  *              of category information.  
  999.  *
  1000.  * PARAMETERS:  category  - new category (index)
  1001.  *
  1002.  * RETURNED:    nothing
  1003.  *
  1004.  * REVISION HISTORY:
  1005.  *         Name   Date      Description
  1006.  *         ----   ----      -----------
  1007.  *         art   6/5/95      Initial Revision
  1008.  *
  1009.  ***********************************************************************/
  1010. static void ChangeCategory (Word category)
  1011. {
  1012.    CurrentCategory = category;
  1013.    TopVisibleRecord = 0;
  1014. }
  1015.  
  1016.  
  1017. /***********************************************************************
  1018.  *
  1019.  * FUNCTION:    SeekRecord
  1020.  *
  1021.  * DESCRIPTION: Given the index of a to do record, this routine scans 
  1022.  *              forewards or backwards for displayable to do records.           
  1023.  *
  1024.  * PARAMETERS:  indexP  - pointer to the index of a record to start from;
  1025.  *                        the index of the record sought is returned in
  1026.  *                        this parameter.
  1027.  *
  1028.  *              offset  - number of records to skip:   
  1029.  *                           0 - mean seek from the current record to the
  1030.  *                             next display record, if the current record is
  1031.  *                             a display record, its index is retuned.
  1032.  *                         1 - mean seek foreward, skipping one displayable 
  1033.  *                             record
  1034.  *                        -1 - means seek backwards, skipping one 
  1035.  *                             displayable record
  1036.  *                             
  1037.  *
  1038.  * RETURNED:    false is return if a displayable record was not found.
  1039.  *
  1040.  * REVISION HISTORY:
  1041.  *         Name   Date      Description
  1042.  *         ----   ----      -----------
  1043.  *         art   6/5/95      Initial Revision
  1044.  *
  1045.  ***********************************************************************/
  1046. static Boolean SeekRecord (UIntPtr indexP, Int offset, Int direction)
  1047. {
  1048.    DmSeekRecordInCategory (AddrDB, indexP, offset, direction, CurrentCategory);
  1049.    if (DmGetLastErr()) return (false);
  1050.    
  1051.    return (true);
  1052. }
  1053.  
  1054.  
  1055. /***********************************************************************
  1056.  *
  1057.  * FUNCTION:    DirtyRecord
  1058.  *
  1059.  * DESCRIPTION: Mark a record dirty (modified).  Record marked dirty 
  1060.  *              will be synchronized.
  1061.  *
  1062.  * PARAMETERS: 
  1063.  *
  1064.  * RETURNED:    nothing
  1065.  *
  1066.  * REVISION HISTORY:
  1067.  *         Name   Date      Description
  1068.  *         ----   ----      -----------
  1069.  *         art   6/5/95      Initial Revision
  1070.  *
  1071.  ***********************************************************************/
  1072. static void DirtyRecord (UInt index)
  1073. {
  1074.    UInt      attr;
  1075.  
  1076.    DmRecordInfo (AddrDB, index, &attr, NULL, NULL);
  1077.    attr |= dmRecAttrDirty;
  1078.    DmSetRecordInfo (AddrDB, index, &attr, NULL);
  1079. }
  1080.  
  1081.  
  1082. /***********************************************************************
  1083.  *
  1084.  * FUNCTION:    CreateNote
  1085.  *
  1086.  * DESCRIPTION: Make sure there is a note field to edit.  If one doesn't
  1087.  * exist make one.  
  1088.  *
  1089.  *   The main reason this routine exists is to make sure we can edit a note
  1090.  * before we close the current form.
  1091.  *
  1092.  * PARAMETERS:  CurrentRecord set
  1093.  *
  1094.  * RETURNED:    true if a note field exists to edit
  1095.  *
  1096.  * REVISION HISTORY:
  1097.  *         Name   Date      Description
  1098.  *         ----   ----      -----------
  1099.  *         roger   10/19/95   Initial Revision
  1100.  *
  1101.  ***********************************************************************/
  1102. static Boolean CreateNote (void)
  1103. {
  1104.    AddrDBRecordType record;
  1105.    AddrDBRecordFlags bit;
  1106.    Handle recordH;
  1107.    Err err;
  1108.  
  1109.    
  1110.    AddrGetRecord (AddrDB, CurrentRecord, &record, &recordH);
  1111.    
  1112.    
  1113.    // Since we are going to edit in place add a note field if there
  1114.    // isn't one
  1115.    if (!record.fields[note])
  1116.       {
  1117.       record.fields[note] = "";
  1118.       bit.allBits = (ULong)1 << note;
  1119.       err = AddrChangeRecord(AddrDB, &CurrentRecord, &record, bit);
  1120.       if (err)
  1121.          {
  1122.          MemHandleUnlock(recordH);
  1123.          FrmAlert(DeviceFullAlert);
  1124.          return false;            // can't make an note field.
  1125.          }
  1126.  
  1127.       }
  1128.    else
  1129.       {
  1130.       MemHandleUnlock(recordH);
  1131.       }
  1132.       
  1133.    return true;                  // a note field exists.
  1134. }
  1135.  
  1136. /***********************************************************************
  1137.  *
  1138.  * FUNCTION:    SelectFont
  1139.  *
  1140.  * DESCRIPTION: This routine handles selection of a font in the List 
  1141.  *              View. 
  1142.  *
  1143.  * PARAMETERS:  currFontID - id of current font
  1144.  *
  1145.  * RETURNED:    id of new font
  1146.  *
  1147.  *
  1148.  * REVISION HISTORY:
  1149.  *            Name    Date        Description
  1150.  *            ----    ----        -----------
  1151.  *            art    9/10/97    Initial Revision
  1152.  *
  1153.  ***********************************************************************/
  1154. static FontID SelectFont (FontID currFontID)
  1155. {
  1156.     Word formID;
  1157.     FontID fontID;
  1158.     
  1159.     formID = (FrmGetFormId (FrmGetActiveForm ()));
  1160.  
  1161.     // Call the OS font selector to get the id of a font.
  1162.     fontID = FontSelect (currFontID);
  1163.  
  1164.     if (fontID != currFontID)
  1165.         FrmUpdateForm (formID, updateFontChanged);
  1166.  
  1167.     return (fontID);
  1168. }
  1169.  
  1170.  
  1171. /***********************************************************************
  1172.  *
  1173.  * FUNCTION:    DetailsSelectCategory
  1174.  *
  1175.  * DESCRIPTION: This routine handles selection, creation and deletion of
  1176.  *              categories form the Details Dialog.  
  1177.  *
  1178.  * PARAMETERS:  nothing
  1179.  *
  1180.  * RETURNED:    true if the category was changed in a way that 
  1181.  *              require the list view to be redrawn.
  1182.  *
  1183.  *              The following global variables are modified:
  1184.  *                     CategoryName
  1185.  *
  1186.  * REVISION HISTORY:
  1187.  *         Name   Date      Description
  1188.  *         ----   ----      -----------
  1189.  *         art   6/5/95   Initial Revision
  1190.  *       art   9/28/95   Fixed problem with merging and renaming
  1191.  *
  1192.  ***********************************************************************/
  1193. static Boolean DetailsSelectCategory (WordPtr category)
  1194. {
  1195.    Boolean categoryEdited;
  1196.    
  1197.    categoryEdited = CategorySelect (AddrDB, FrmGetActiveForm (),
  1198.       DetailsCategoryTrigger, DetailsCategoryList,
  1199.       false, category, CategoryName, 1, 0);
  1200.    
  1201.    return (categoryEdited);
  1202. }
  1203.  
  1204.  
  1205. /***********************************************************************
  1206.  *
  1207.  * FUNCTION:    DeleteRecord
  1208.  *
  1209.  * DESCRIPTION: Deletes an address record.
  1210.  *
  1211.  * PARAMETERS:  nothing
  1212.  *
  1213.  * RETURNED:    nothing
  1214.  *
  1215.  * REVISION HISTORY:
  1216.  *         Name   Date      Description
  1217.  *         ----   ----      -----------
  1218.  *         roger   6/13/95   Initial Revision
  1219.  *
  1220.  ***********************************************************************/
  1221. static void DeleteRecord (Boolean archive)
  1222. {
  1223.    // Show the prior record.  Users want to see where the record was and
  1224.    // they also want to return to the same location in the database because
  1225.    // they might be working their way through the records.  If there isn't
  1226.    // a prior record show the following record.  If there isn't a following
  1227.    // record then don't show a record.
  1228.    ListViewSelectThisRecord = CurrentRecord;
  1229.    if (!SeekRecord(&ListViewSelectThisRecord, 1, dmSeekBackward))
  1230.       if (!SeekRecord(&ListViewSelectThisRecord, 1, dmSeekForward))
  1231.          ListViewSelectThisRecord = noRecord;
  1232.    
  1233.    // Delete or archive the record.
  1234.    if (archive)
  1235.       DmArchiveRecord (AddrDB, CurrentRecord);
  1236.    else
  1237.       DmDeleteRecord (AddrDB, CurrentRecord);
  1238.    
  1239.    // Deleted records are stored at the end of the database
  1240.    DmMoveRecord (AddrDB, CurrentRecord, DmNumRecords (AddrDB));
  1241.  
  1242.    // Since we just moved the CurrentRecord to the end the 
  1243.    // ListViewSelectThisRecord may have been moved up one position.
  1244.    if (ListViewSelectThisRecord >= CurrentRecord &&
  1245.       ListViewSelectThisRecord != noRecord)
  1246.       ListViewSelectThisRecord--;
  1247.    
  1248.  
  1249.    // Use whatever record we found to select.   
  1250.    CurrentRecord = ListViewSelectThisRecord;
  1251. }
  1252.  
  1253.  
  1254. /***********************************************************************
  1255.  *
  1256.  * FUNCTION:    DetailsDeleteRecord
  1257.  *
  1258.  * DESCRIPTION: This routine deletes an address record. This routine is 
  1259.  *              called when the delete button in the details dialog is
  1260.  *              pressed or when delete record is used from a menu.
  1261.  *
  1262.  * PARAMETERS:  nothing
  1263.  *
  1264.  * RETURNED:    true if the record was delete or archived.
  1265.  *
  1266.  * REVISION HISTORY:
  1267.  *         Name   Date      Description
  1268.  *         ----   ----      -----------
  1269.  *         art   6/5/95   Initial Revision
  1270.  *         art   9/30/95   Remember the "save backup" settting.
  1271.  *
  1272.  ***********************************************************************/
  1273. static Boolean DetailsDeleteRecord (void)
  1274. {
  1275.    Word ctlIndex;
  1276.    Word buttonHit;
  1277.    FormPtr alert;
  1278.    Boolean archive;
  1279.       
  1280.    // Display an alert to comfirm the operation.
  1281.    alert = FrmInitForm (DeleteAddrDialog);
  1282.  
  1283.    // Set the "save backup" checkbox to its previous setting.
  1284.    ctlIndex = FrmGetObjectIndex (alert, DeleteAddrSaveBackup);
  1285.    FrmSetControlValue (alert, ctlIndex, SaveBackup);
  1286.  
  1287.    buttonHit = FrmDoDialog (alert);
  1288.  
  1289.    archive = FrmGetControlValue (alert, ctlIndex);
  1290.  
  1291.    FrmDeleteForm (alert);
  1292.    if (buttonHit == DeleteAddrCancel)
  1293.       return (false);
  1294.  
  1295.    // Remember the "save backup" checkbox setting.
  1296.    SaveBackup = archive;
  1297.  
  1298.    DeleteRecord(archive);
  1299.    
  1300.    return (true);
  1301. }
  1302.  
  1303.  
  1304. /***********************************************************************
  1305.  *
  1306.  * FUNCTION:    DetailsApply
  1307.  *
  1308.  * DESCRIPTION: This routine applies the changes made in the Details Dialog.
  1309.  *
  1310.  * PARAMETERS:  category - new catagory
  1311.  *
  1312.  * RETURNED:    code which indicates how the to do list was changed,  this
  1313.  *              code is send as the update code, in the frmUpdate event.
  1314.  *
  1315.  * REVISION HISTORY:
  1316.  *         Name   Date      Description
  1317.  *         ----   ----      -----------
  1318.  *         art   6/5/95   Initial Revision
  1319.  *         kcr   10/9/95   added 'private records' alert
  1320.  *
  1321.  ***********************************************************************/
  1322. static Word DetailsApply (Word category, Boolean categoryEdited)
  1323. {
  1324.    UInt               attr;
  1325.    Word                updateCode = 0;
  1326.    Boolean           secret;
  1327.    Boolean            dirty = false;
  1328.    AddrDBRecordType currentRecord;
  1329.    Handle recordH;
  1330.    AddrDBRecordFlags changedFields;
  1331.    UInt               newPhoneFieldToDisplay;
  1332.    Err               err;
  1333.  
  1334.  
  1335.  
  1336.    // Get the phone number to show at the list view.
  1337.    AddrGetRecord(AddrDB, CurrentRecord, ¤tRecord, &recordH);
  1338.    newPhoneFieldToDisplay = LstGetSelection(GetObjectPtr (DetailsPhoneList));
  1339.    if (currentRecord.options.phones.displayPhoneForList != newPhoneFieldToDisplay)
  1340.       {
  1341.       currentRecord.options.phones.displayPhoneForList = newPhoneFieldToDisplay;
  1342.       changedFields.allBits = 0;
  1343.       err = AddrChangeRecord(AddrDB, &CurrentRecord, ¤tRecord,
  1344.          changedFields);
  1345.       if (err)
  1346.          {
  1347.          MemHandleUnlock(recordH);
  1348.          FrmAlert(DeviceFullAlert);
  1349.          }
  1350.  
  1351.       updateCode |= updateListViewPhoneChanged;
  1352.       }
  1353.    else
  1354.       MemHandleUnlock(recordH);
  1355.    
  1356.    
  1357.    // Get the category and the secret attribute of the current record.
  1358.    DmRecordInfo (AddrDB, CurrentRecord, &attr, NULL, NULL);   
  1359.  
  1360.  
  1361.    // Get the current setting of the secret checkbox and compare it the
  1362.    // the setting of the record.  Update the record if the values 
  1363.    // are different.  If the record is being set 'secret' for the
  1364.    //   first time, and the system 'hide secret records' setting is
  1365.    //   off, display an informational alert to the user.
  1366.    secret = CtlGetValue (GetObjectPtr (DetailsSecretCheckbox));
  1367.    if (((attr & dmRecAttrSecret) == dmRecAttrSecret) != secret)
  1368.       {
  1369.       if (HideSecretRecords)
  1370.          updateCode |= updateItemHide;
  1371.       else if (secret)
  1372.          FrmAlert (privateRecordInfoAlert);
  1373.       
  1374.       dirty = true;
  1375.       
  1376.       if (secret)
  1377.          attr |= dmRecAttrSecret;
  1378.       else
  1379.          attr &= ~dmRecAttrSecret;
  1380.       }
  1381.  
  1382.  
  1383.    // Compare the current category to the category setting of the dialog.
  1384.    // Update the record if the category are different.   
  1385.    if ((attr & dmRecAttrCategoryMask) != category)
  1386.       {
  1387.       attr &= ~dmRecAttrCategoryMask;
  1388.       attr |= category;
  1389.       dirty = true;
  1390.  
  1391.       CurrentCategory = category;
  1392.       updateCode |= updateCategoryChanged;
  1393.       }
  1394.  
  1395.    // If current category was moved, deleted renamed, or merged with
  1396.    // another category, then the list view needs to be redrawn.
  1397.    if (categoryEdited)
  1398.       {
  1399.       CurrentCategory = category;
  1400.       updateCode |= updateCategoryChanged;
  1401.       }
  1402.          
  1403.  
  1404.    // Save the new category and/or secret status, and mark the record dirty.
  1405.    if (dirty)
  1406.       {
  1407.       attr |= dmRecAttrDirty;
  1408.       DmSetRecordInfo (AddrDB, CurrentRecord, &attr, NULL);
  1409.       }
  1410.  
  1411.  
  1412.    return (updateCode);
  1413. }
  1414.  
  1415.  
  1416. /***********************************************************************
  1417.  *
  1418.  * FUNCTION:    DetailsInit
  1419.  *
  1420.  * DESCRIPTION: This routine initializes the Details Dialog.  
  1421.  *
  1422.  * PARAMETERS:  nothing
  1423.  *
  1424.  * RETURNED:    nothing
  1425.  *
  1426.  * REVISION HISTORY:
  1427.  *         Name   Date      Description
  1428.  *         ----   ----      -----------
  1429.  *         art   6/5/95      Initial Revision
  1430.  *
  1431.  ***********************************************************************/
  1432. static void DetailsInit (WordPtr categoryP)
  1433. {
  1434.    UInt attr;
  1435.    UInt category;
  1436.    ControlPtr ctl;
  1437.    ListPtr popupPhoneList;
  1438.    AddrDBRecordType currentRecord;
  1439.    Handle recordH;
  1440.    AddrAppInfoPtr appInfoPtr;
  1441.    UInt phoneLabel;
  1442.    UInt i;
  1443.  
  1444.  
  1445.    // Make a list of the phone labels used by this record
  1446.    appInfoPtr = (AddrAppInfoPtr) AddrAppInfoGetPtr(AddrDB);
  1447.    AddrGetRecord(AddrDB, CurrentRecord, ¤tRecord, &recordH);
  1448.  
  1449.    for (i = 0; i < 5; i++)
  1450.       {
  1451.       phoneLabel = GetPhoneLabel(¤tRecord, firstPhoneField + i);
  1452.       DetailsPhoneListChoices[i] = appInfoPtr->fieldLabels[phoneLabel + 
  1453.          ((phoneLabel < numPhoneLabelsStoredFirst) ? 
  1454.          firstPhoneField : (addressFieldsCount - numPhoneLabelsStoredFirst))];
  1455.       }
  1456.              
  1457.  
  1458.    // Set the default phone list to the list of phone labels just made
  1459.    popupPhoneList = GetObjectPtr (DetailsPhoneList);
  1460.    LstSetListChoices(popupPhoneList, DetailsPhoneListChoices, numPhoneFields);
  1461.    LstSetSelection (popupPhoneList, currentRecord.options.phones.displayPhoneForList);
  1462.    LstSetHeight (popupPhoneList, numPhoneFields);
  1463.    CtlSetLabel(GetObjectPtr (DetailsPhoneTrigger),
  1464.       LstGetSelectionText (popupPhoneList, currentRecord.options.phones.displayPhoneForList));
  1465.  
  1466.    // If the record is mark secret, turn on the secret checkbox.
  1467.    DmRecordInfo (AddrDB, CurrentRecord, &attr, NULL, NULL);
  1468.    ctl = GetObjectPtr (DetailsSecretCheckbox);
  1469.    CtlSetValue (ctl, attr & dmRecAttrSecret);
  1470.  
  1471.    // Set the label of the category trigger.
  1472.    category = attr & dmRecAttrCategoryMask;
  1473.    CategoryGetName (AddrDB, category, CategoryName);
  1474.    ctl = GetObjectPtr (DetailsCategoryTrigger);
  1475.    CategorySetTriggerLabel (ctl, CategoryName);
  1476.  
  1477.    // Return the current category and due date.
  1478.    *categoryP = category;
  1479.  
  1480.  
  1481.    MemHandleUnlock(recordH);
  1482.    MemPtrUnlock(appInfoPtr);
  1483. }
  1484.  
  1485.  
  1486. /***********************************************************************
  1487.  *
  1488.  * FUNCTION:    DetailsHandleEvent
  1489.  *
  1490.  * DESCRIPTION: This routine is the event handler for the "Details
  1491.  *              Dialog Box" of the Address application.
  1492.  *
  1493.  * PARAMETERS:  event  - a pointer to an EventType structure
  1494.  *
  1495.  * RETURNED:    true if the event has handle and should not be passed
  1496.  *              to a higher level handler.
  1497.  *
  1498.  * REVISION HISTORY:
  1499.  *         Name   Date      Description
  1500.  *         ----   ----      -----------
  1501.  *         art   6/5/95      Initial Revision
  1502.  *
  1503.  ***********************************************************************/
  1504. static Boolean DetailsHandleEvent (EventPtr event)
  1505. {
  1506.    static Word       category;
  1507.    static Boolean      categoryEdited;
  1508.  
  1509.    Word updateCode;
  1510.    Boolean handled = false;
  1511.    FormPtr frm;
  1512.  
  1513.  
  1514.    if (event->eType == ctlSelectEvent)
  1515.       {
  1516.       switch (event->data.ctlSelect.controlID)
  1517.          {
  1518.          case DetailsOkButton:
  1519.             updateCode = DetailsApply (category, categoryEdited);
  1520.             LeaveForm ();
  1521.             if (updateCode)
  1522.                 FrmUpdateForm (EditView, updateCode);
  1523.             handled = true;
  1524.             break;
  1525.  
  1526.          case DetailsCancelButton:
  1527.             if (categoryEdited)
  1528.                 FrmUpdateForm (EditView, updateCategoryChanged);
  1529.             LeaveForm ();
  1530.             handled = true;
  1531.             break;
  1532.             
  1533.          case DetailsDeleteButton:
  1534.             if ( DetailsDeleteRecord ())
  1535.                { 
  1536.                FrmCloseAllForms ();
  1537.                FrmGotoForm (ListView);
  1538.                }
  1539.             handled = true;
  1540.             break;
  1541.             
  1542.          case DetailsNoteButton:
  1543.             DetailsApply (category, categoryEdited);
  1544.             FrmReturnToForm (EditView);
  1545.             if (CreateNote())
  1546.                 {
  1547.                FrmGotoForm (NoteView);
  1548.                RecordNeededAfterEditView = true;
  1549.                }
  1550.             handled = true;
  1551.             break;
  1552.  
  1553.          case DetailsCategoryTrigger:
  1554.             categoryEdited = DetailsSelectCategory (&category);
  1555.             handled = true;
  1556.             break;
  1557.          }
  1558.       }
  1559.  
  1560.  
  1561.    else if (event->eType == frmOpenEvent)
  1562.       {
  1563.       frm = FrmGetActiveForm ();
  1564.       DetailsInit (&category);
  1565.       FrmDrawForm (frm);
  1566.       categoryEdited = false;
  1567.       handled = true;
  1568.       }
  1569.  
  1570.    return (handled);
  1571. }
  1572.  
  1573.  
  1574. /***********************************************************************
  1575.  *
  1576.  * FUNCTION:    CustomEditSave
  1577.  *
  1578.  * DESCRIPTION: Write the renamed field labels
  1579.  *
  1580.  * PARAMETERS:  frm
  1581.  *
  1582.  * RETURNED:    nothing
  1583.  *
  1584.  * REVISION HISTORY:
  1585.  *         Name   Date      Description
  1586.  *         ----   ----      -----------
  1587.  *         roger   2/23/95   Initial Revision
  1588.  *
  1589.  ***********************************************************************/
  1590. static void CustomEditSave (FormPtr frm)
  1591. {
  1592.     Word      index;
  1593.     FieldPtr   fld;
  1594.     Word      objNumber;
  1595.     CharPtr textP;
  1596.     AddrAppInfoPtr appInfoPtr;
  1597.     Boolean sendUpdate = false;
  1598.  
  1599.  
  1600.     // Get the object number of the first field.
  1601.     objNumber = FrmGetObjectIndex (frm, CustomEditFirstField);
  1602.  
  1603.  
  1604.     // For each dirty field update the corresponding label.
  1605.     for (index = firstRenameableLabel; index <= lastRenameableLabel; index++)
  1606.         {
  1607.         fld = FrmGetObjectPtr (frm, objNumber++);
  1608.         if (FldDirty(fld))
  1609.             {
  1610.             sendUpdate = true;
  1611.             textP = FldGetTextPtr(fld);
  1612.             if (textP)
  1613.             AddrSetFieldLabel(AddrDB, index, textP);
  1614.             }
  1615.         }
  1616.  
  1617.     if (sendUpdate)
  1618.         {
  1619.         // Update the column width since a label changed.
  1620.         appInfoPtr = (AddrAppInfoPtr) AddrAppInfoGetPtr(AddrDB);
  1621.         EditLabelColumnWidth = GetLabelColumnWidth (appInfoPtr, stdFont);
  1622.         RecordLabelColumnWidth = GetLabelColumnWidth (appInfoPtr, AddrRecordFont);
  1623.         MemPtrUnlock(appInfoPtr);
  1624.  
  1625.         FrmUpdateForm (0, updateCustomFieldLabelChanged);
  1626.         }
  1627.  
  1628. }
  1629.  
  1630.  
  1631. /***********************************************************************
  1632.  *
  1633.  * FUNCTION:    CustomEditInit
  1634.  *
  1635.  * DESCRIPTION: Load field labels for editing.
  1636.  *
  1637.  * PARAMETERS:  frm
  1638.  *
  1639.  * RETURNED:    nothing
  1640.  *
  1641.  * REVISION HISTORY:
  1642.  *         Name   Date      Description
  1643.  *         ----   ----      -----------
  1644.  *         roger   2/23/95   Initial Revision
  1645.  *
  1646.  ***********************************************************************/
  1647. static void CustomEditInit (FormPtr frm)
  1648. {
  1649.    Word      index;
  1650.    Word      length;
  1651.    FieldPtr   fld;
  1652.    Word      objNumber;
  1653.    Handle textH;
  1654.    CharPtr textP;
  1655.    AddrAppInfoPtr appInfoPtr;
  1656.    addressLabel *fieldLabels;
  1657.    
  1658.    
  1659.    // Get the object number of the first field.
  1660.    objNumber = FrmGetObjectIndex (frm, CustomEditFirstField);
  1661.  
  1662.    appInfoPtr = (AddrAppInfoPtr) AddrAppInfoGetPtr(AddrDB);
  1663.    fieldLabels = appInfoPtr->fieldLabels;
  1664.  
  1665.    // For each label, allocate some global heap space and copy the
  1666.    // the string to the global heap.  Then set a field to use the
  1667.    // copied string for editing.  If the field is unused no space is
  1668.    // allocated.  The field will allocate space if text is typed in.
  1669.    for (index = firstRenameableLabel; index <= lastRenameableLabel; index++)
  1670.       {
  1671.       fld = FrmGetObjectPtr (frm, objNumber++);
  1672.       length = StrLen(fieldLabels[index]);
  1673.       if (length > 0)
  1674.          {
  1675.          length += 1;         // include space for a null terminator
  1676.          textH = MemHandleNew(length);
  1677.          if (textH)
  1678.             {
  1679.             textP = MemHandleLock(textH);
  1680.             MemMove(textP, fieldLabels[index], length);
  1681.             FldSetTextHandle (fld, textH);
  1682.             MemHandleUnlock(textH);
  1683.             }
  1684.          }
  1685.       }
  1686.       
  1687.    MemPtrUnlock(appInfoPtr);
  1688. }
  1689.  
  1690.  
  1691. /***********************************************************************
  1692.  *
  1693.  * FUNCTION:    CustomEditHandleEvent
  1694.  *
  1695.  * DESCRIPTION: This routine is the event handler for the "Edit Custom
  1696.  *              Fields" of the Address application.
  1697.  *
  1698.  * PARAMETERS:  event  - a pointer to an EventType structure
  1699.  *
  1700.  * RETURNED:    true if the event has handle and should not be passed
  1701.  *              to a higher level handler.
  1702.  *
  1703.  * REVISION HISTORY:
  1704.  *         Name   Date      Description
  1705.  *         ----   ----      -----------
  1706.  *         roger   6/23/95   Initial Revision
  1707.  *
  1708.  ***********************************************************************/
  1709. static Boolean CustomEditHandleEvent (EventPtr event)
  1710. {
  1711.    Boolean handled = false;
  1712.    FormPtr frm;
  1713.  
  1714.  
  1715.    if (event->eType == ctlSelectEvent)
  1716.       {
  1717.       switch (event->data.ctlSelect.controlID)
  1718.          {
  1719.          case CustomEditOkButton:
  1720.             frm = FrmGetActiveForm();
  1721.             CustomEditSave(frm);
  1722.             LeaveForm();
  1723.             handled = true;
  1724.             break;
  1725.  
  1726.          case CustomEditCancelButton:
  1727.             LeaveForm();
  1728.             handled = true;
  1729.             break;
  1730.          default:
  1731.             break;
  1732.          
  1733.          }
  1734.       }
  1735.  
  1736.  
  1737.    else if (event->eType == frmOpenEvent)
  1738.       {
  1739.       frm = FrmGetActiveForm ();
  1740.       CustomEditInit (frm);
  1741.       FrmDrawForm (frm);
  1742.       handled = true;
  1743.       }
  1744.  
  1745.    return (handled);
  1746. }
  1747.  
  1748.  
  1749. /***********************************************************************
  1750.  *
  1751.  * FUNCTION:    PreferencesDialogSave
  1752.  *
  1753.  * DESCRIPTION: Write the renamed field labels
  1754.  *
  1755.  * PARAMETERS:  frm
  1756.  *
  1757.  * RETURNED:    nothing
  1758.  *
  1759.  * REVISION HISTORY:
  1760.  *         Name   Date      Description
  1761.  *         ----   ----      -----------
  1762.  *         roger   8/2/95   Initial Revision
  1763.  *
  1764.  ***********************************************************************/
  1765. static void PreferencesDialogSave (FormPtr frm)
  1766. {
  1767.    FormPtr curFormP;
  1768.    FormPtr formP;
  1769.    Boolean sortByCompany;
  1770.    
  1771.    
  1772.    RememberLastCategory = CtlGetValue(FrmGetObjectPtr (frm, 
  1773.       FrmGetObjectIndex (frm, PreferencesRememberCategoryCheckbox)));
  1774.    
  1775.    
  1776.    sortByCompany = CtlGetValue(FrmGetObjectPtr (frm, FrmGetObjectIndex (frm, PreferencesCompanyName)));
  1777.    
  1778.    if (sortByCompany != SortByCompany)
  1779.       {
  1780.       // Put up the sorting message dialog so the user knows what's going on
  1781.       // while the sort locks up the device.
  1782.       curFormP = FrmGetActiveForm ();
  1783.       formP = FrmInitForm (SortingMessageDialog);
  1784.       FrmSetActiveForm (formP);
  1785.       FrmDrawForm (formP);
  1786.       
  1787.       // Peform the sort
  1788.       SortByCompany = sortByCompany;
  1789.       AddrChangeSortOrder(AddrDB, SortByCompany);
  1790.       CurrentRecord = 0;
  1791.       TopVisibleRecord = 0;
  1792.       FrmUpdateForm (ListView, updateRedrawAll);
  1793.  
  1794.       
  1795.       // Remove the sorting message
  1796.       FrmDeleteForm (formP);
  1797.       FrmSetActiveForm (curFormP);
  1798.       }
  1799.  
  1800. }
  1801.  
  1802.  
  1803. /***********************************************************************
  1804.  *
  1805.  * FUNCTION:    PreferencesDialogInit
  1806.  *
  1807.  * DESCRIPTION: Initialize the dialog's ui.  Sets the database sort by
  1808.  * buttons.
  1809.  *
  1810.  * PARAMETERS:  frm
  1811.  *
  1812.  * RETURNED:    nothing
  1813.  *
  1814.  * REVISION HISTORY:
  1815.  *         Name   Date      Description
  1816.  *         ----   ----      -----------
  1817.  *         roger   8/2/95   Initial Revision
  1818.  *
  1819.  ***********************************************************************/
  1820. static void PreferencesDialogInit (FormPtr frm)
  1821. {
  1822.    Word      objNumber;
  1823.    
  1824.    
  1825.    CtlSetValue(FrmGetObjectPtr (frm, FrmGetObjectIndex (frm, PreferencesRememberCategoryCheckbox)),
  1826.       RememberLastCategory);
  1827.    
  1828.    
  1829.    // Set the current sort by setting
  1830.    if (SortByCompany)
  1831.       objNumber = PreferencesCompanyName;
  1832.    else
  1833.       objNumber = PreferencesLastName;
  1834.  
  1835.    CtlSetValue(FrmGetObjectPtr (frm, FrmGetObjectIndex (frm, objNumber)),
  1836.       true);
  1837.    
  1838. }
  1839.  
  1840.  
  1841. /***********************************************************************
  1842.  *
  1843.  * FUNCTION:    PreferencesDialogHandleEvent
  1844.  *
  1845.  * DESCRIPTION: This routine is the event handler for the "List By
  1846.  *              Dialog" of the Address application.
  1847.  *
  1848.  * PARAMETERS:  event  - a pointer to an EventType structure
  1849.  *
  1850.  * RETURNED:    true if the event has handle and should not be passed
  1851.  *              to a higher level handler.
  1852.  *
  1853.  * REVISION HISTORY:
  1854.  *         Name   Date      Description
  1855.  *         ----   ----      -----------
  1856.  *         roger   8/2/95   Initial Revision
  1857.  *
  1858.  ***********************************************************************/
  1859. static Boolean PreferencesDialogHandleEvent (EventPtr event)
  1860. {
  1861.    Boolean handled = false;
  1862.    FormPtr frm;
  1863.  
  1864.  
  1865.    if (event->eType == ctlSelectEvent)
  1866.       {
  1867.       switch (event->data.ctlSelect.controlID)
  1868.          {
  1869.          case PreferencesOkButton:
  1870.             frm = FrmGetActiveForm();
  1871.             PreferencesDialogSave(frm);
  1872.             LeaveForm();
  1873.             handled = true;
  1874.             break;
  1875.  
  1876.          case PreferencesCancelButton:
  1877.             LeaveForm();
  1878.             handled = true;
  1879.             break;
  1880.          default:
  1881.             break;
  1882.             
  1883.          }
  1884.       }
  1885.  
  1886.  
  1887.    else if (event->eType == frmOpenEvent)
  1888.       {
  1889.       frm = FrmGetActiveForm ();
  1890.       PreferencesDialogInit (frm);
  1891.       FrmDrawForm (frm);
  1892.       handled = true;
  1893.       }
  1894.  
  1895.    return (handled);
  1896. }
  1897.  
  1898.  
  1899. #pragma mark ----------------
  1900. /***********************************************************************
  1901.  *
  1902.  * FUNCTION:    NoteViewDrawTitle
  1903.  *
  1904.  * DESCRIPTION: Draw the title of the note view.  The title should be
  1905.  * the names that appear for the record on the list view.
  1906.  *
  1907.  * PARAMETERS:  nothing
  1908.  *
  1909.  * RETURNED:    nothing
  1910.  *
  1911.  * REVISION HISTORY:
  1912.  *         Name   Date      Description
  1913.  *         ----   ----      -----------
  1914.  *         roger   6/21/95   Initial Revision
  1915.  *
  1916.  ***********************************************************************/
  1917.  static void NoteViewDrawTitle (void)
  1918.  {
  1919.    SWord x, y;
  1920.    Err error;
  1921.    AddrDBRecordType record;
  1922.    Handle recordH;
  1923.    UInt fieldSeparatorWidth;
  1924.    UInt shortenedFieldWidth;
  1925.    CharPtr name1, name2;
  1926.    Int name1Length;
  1927.    Int name2Length;
  1928.    Int name1Width;
  1929.    Int name2Width;
  1930.    UInt nameExtent;
  1931.    short winWidth;
  1932.    short winHeight;
  1933.    FontID curFont;
  1934.    RectangleType r;
  1935.     Boolean name1HasPriority;
  1936.  
  1937.    
  1938.    curFont = FntSetFont (boldFont);
  1939.    WinGetWindowExtent (&winWidth, &winHeight);
  1940.    x = 2;
  1941.    y = 1;
  1942.    nameExtent = winWidth - 4;
  1943.    
  1944.    error = AddrGetRecord(AddrDB, CurrentRecord, &record, &recordH);
  1945.    ErrNonFatalDisplayIf ((error), "Record not found");
  1946.    if (error) return;
  1947.  
  1948.       
  1949.    name1HasPriority = DetermineRecordName(&record, &shortenedFieldWidth, &fieldSeparatorWidth, 
  1950.       SortByCompany, &name1, &name1Length, &name1Width, 
  1951.       &name2, &name2Length, &name2Width, &UnnamedRecordStringPtr, nameExtent);
  1952.  
  1953.    DrawRecordName(name1, name1Length, name1Width, name2, name2Length, name2Width,
  1954.       nameExtent, &x, y, shortenedFieldWidth, fieldSeparatorWidth, true,
  1955.       name1HasPriority || !SortByCompany);
  1956.  
  1957.    RctSetRectangle (&r, 0, 0, winWidth, FntLineHeight()+2 );
  1958.    WinInvertRectangle (&r, 3);
  1959.  
  1960.    MemHandleUnlock(recordH);
  1961.    FntSetFont (curFont);
  1962.  }
  1963.  
  1964.  
  1965. /***********************************************************************
  1966.  *
  1967.  * FUNCTION:    NoteViewUpdateScrollBar
  1968.  *
  1969.  * DESCRIPTION: This routine update the scroll bar.
  1970.  *
  1971.  * PARAMETERS:  nothing
  1972.  *
  1973.  * RETURNED:    nothing
  1974.  *
  1975.  * REVISION HISTORY:
  1976.  *         Name   Date      Description
  1977.  *         ----   ----      -----------
  1978.  *         art   7/1/96   Initial Revision
  1979.  *
  1980.  ***********************************************************************/
  1981. static void NoteViewUpdateScrollBar (void)
  1982. {
  1983.    Word scrollPos;
  1984.    Word textHeight;
  1985.    Word fieldHeight;
  1986.    Short maxValue;
  1987.    FieldPtr fld;
  1988.    ScrollBarPtr bar;
  1989.  
  1990.    fld = GetObjectPtr (NoteField);
  1991.    bar = GetObjectPtr (NoteScrollBar);
  1992.    
  1993.    FldGetScrollValues (fld, &scrollPos, &textHeight,  &fieldHeight);
  1994.  
  1995.    if (textHeight > fieldHeight)
  1996.       maxValue = textHeight - fieldHeight;
  1997.    else if (scrollPos)
  1998.       maxValue = scrollPos;
  1999.    else
  2000.       maxValue = 0;
  2001.  
  2002.    SclSetScrollBar (bar, scrollPos, 0, maxValue, fieldHeight-1);
  2003. }
  2004.  
  2005.  
  2006.  
  2007.  
  2008. /***********************************************************************
  2009.  *
  2010.  * FUNCTION:    NoteViewChangeFont
  2011.  *
  2012.  * DESCRIPTION: This routine changes the font used to display a note
  2013.  *
  2014.  * PARAMETERS:  nothing
  2015.  *
  2016.  * RETURNED:    nothing
  2017.  *
  2018.  * REVISION HISTORY:
  2019.  *         Name   Date      Description
  2020.  *         ----   ----      -----------
  2021.  *         art   6/5/95      Initial Revision
  2022.  *
  2023.  ***********************************************************************/
  2024. /*
  2025. static void NoteViewChangeFont (UInt controlID)
  2026. {
  2027.     FontID fontID;
  2028.    FieldPtr fld;
  2029.    
  2030.    
  2031.    fld = GetObjectPtr (NoteField);
  2032.     fontID = FldGetFont (fld);
  2033.    if (controlID == NoteSmallFontButton)
  2034.       NoteFont = stdFont;
  2035.    else
  2036.       NoteFont = largeBoldFont;
  2037.       
  2038.    // FldSetFont will redraw the field if it is visible.
  2039.    FldSetFont (fld, NoteFont);
  2040.    
  2041.    NoteViewUpdateScrollBar ();
  2042. }
  2043. */
  2044.  
  2045.  
  2046. /***********************************************************************
  2047.  *
  2048.  * FUNCTION:    NoteViewLoadRecord
  2049.  *
  2050.  * DESCRIPTION: Load the record's note field into the field object
  2051.  * for editing in place.  The note field is too big (4K) to edit in 
  2052.  * the heap.
  2053.  *
  2054.  * PARAMETERS:  nothing
  2055.  *
  2056.  * RETURNED:    nothing
  2057.  *
  2058.  * REVISION HISTORY:
  2059.  *         Name   Date      Description
  2060.  *         ----   ----      -----------
  2061.  *         roger   6/21/95   Initial Revision
  2062.  *         roger   8/25/95   Changed to edit in place
  2063.  *
  2064.  ***********************************************************************/
  2065. static void NoteViewLoadRecord (void)
  2066. {
  2067.    FieldPtr fld;
  2068.    AddrDBRecordType record;
  2069.    Handle recordH;
  2070.    Word offset;
  2071.    CharPtr ptr;
  2072.  
  2073.    
  2074.    // Get a pointer to the memo field.
  2075.    fld = GetObjectPtr (NoteField);
  2076.    
  2077.    // Set the font used in the memo field.
  2078.    FldSetFont (fld, NoteFont);
  2079.    
  2080.  
  2081.    AddrGetRecord (AddrDB, CurrentRecord, &record, &recordH);
  2082.    
  2083.    
  2084.    // CreateNote will have been called before the NoteView was switched
  2085.    // to.  It will have insured that a note field exists.
  2086.       
  2087.    
  2088.    // Find out where the note field is to edit it
  2089.    ptr = MemDeref (recordH);
  2090.    offset = record.fields[note] - ptr;
  2091.    FldSetText (fld, recordH, offset, StrLen(record.fields[note])+1);
  2092.    
  2093.    MemHandleUnlock(recordH);
  2094. }
  2095.  
  2096.  
  2097. /***********************************************************************
  2098.  *
  2099.  * FUNCTION:    NoteViewSave
  2100.  *
  2101.  * DESCRIPTION: This routine 
  2102.  *
  2103.  * PARAMETERS:  nothing
  2104.  *
  2105.  * RETURNED:    nothing
  2106.  *
  2107.  * REVISION HISTORY:
  2108.  *         Name   Date      Description
  2109.  *         ----   ----      -----------
  2110.  *         art   6/5/95   Initial Revision
  2111.  *         roger   8/25/95   Changed to edit in place
  2112.  *
  2113.  ***********************************************************************/
  2114. static void NoteViewSave (void)
  2115. {
  2116.    FieldPtr fld;
  2117.    int textLength;
  2118.  
  2119.    
  2120.    fld = GetObjectPtr (NoteField);
  2121.  
  2122.    
  2123.    // If the field wasn't modified then don't do anything
  2124.    if (FldDirty (fld))
  2125.       {      
  2126.       // Release any free space in the note field.
  2127.       FldCompactText (fld);
  2128.  
  2129.       DirtyRecord (CurrentRecord);
  2130.       }
  2131.  
  2132.       
  2133.    textLength = FldGetTextLength(fld);
  2134.  
  2135.    // Clear the handle value in the field, otherwise the handle
  2136.    // will be free when the form is disposed of,  this call also unlocks
  2137.    // the handle that contains the note string.
  2138.    FldSetTextHandle (fld, 0);
  2139.    
  2140.    
  2141.    // Empty fields are not allowed because they cause problems
  2142.    if (textLength == 0)
  2143.       DeleteNote();
  2144. }
  2145.  
  2146.  
  2147. /***********************************************************************
  2148.  *
  2149.  * FUNCTION:    DeleteNote
  2150.  *
  2151.  * DESCRIPTION: Deletes the note field from the current record.
  2152.  *
  2153.  * PARAMETERS:  nothing
  2154.  *
  2155.  * RETURNED:    nothing
  2156.  *
  2157.  * REVISION HISTORY:
  2158.  *         Name   Date      Description
  2159.  *         ----   ----      -----------
  2160.  *         roger   6/21/95   Initial Revision
  2161.  *
  2162.  ***********************************************************************/
  2163. static void DeleteNote (void)
  2164. {
  2165.    AddrDBRecordType record;
  2166.    Handle recordH;
  2167.    AddrDBRecordFlags changedField;
  2168.    Err err;
  2169.    
  2170.       
  2171.    AddrGetRecord (AddrDB, CurrentRecord, &record, &recordH);
  2172.    record.fields[note] = NULL;
  2173.    changedField.allBits = (ULong)1 << note;
  2174.    err = AddrChangeRecord(AddrDB, &CurrentRecord, &record, changedField);
  2175.    if (err)
  2176.       {
  2177.       MemHandleUnlock(recordH);
  2178.       FrmAlert(DeviceFullAlert);
  2179.       return;
  2180.       }
  2181.  
  2182.  
  2183.    // Mark the record dirty.   
  2184.    DirtyRecord (CurrentRecord);
  2185. }
  2186.  
  2187.  
  2188. /***********************************************************************
  2189.  *
  2190.  * FUNCTION:    NoteViewDeleteNote
  2191.  *
  2192.  * DESCRIPTION: This routine deletes a the note field from a to do record.
  2193.  *
  2194.  * PARAMETERS:  nothing
  2195.  *
  2196.  * RETURNED:    true if the note was deleted.
  2197.  *
  2198.  * REVISION HISTORY:
  2199.  *         Name   Date      Description
  2200.  *         ----   ----      -----------
  2201.  *         art   6/5/95      Initial Revision
  2202.  *
  2203.  ***********************************************************************/
  2204. static Boolean NoteViewDeleteNote (void)
  2205. {
  2206.    FieldPtr fld;
  2207.    
  2208.    if (FrmAlert(DeleteNoteAlert) != DeleteNoteYes)
  2209.       return (false);
  2210.       
  2211.    // Unlock the handle that contains the text of the memo.
  2212.    fld = GetObjectPtr (NoteField);
  2213.    ErrFatalDisplayIf ((! fld), "Bad field");
  2214.  
  2215.    // Clear the handle value in the field, otherwise the handle
  2216.    // will be free when the form is disposed of. this call also 
  2217.    // unlocks the handle the contains the note string.
  2218.    FldCompactText (fld);
  2219.    FldSetTextHandle (fld, 0);   
  2220.  
  2221.  
  2222.    DeleteNote();
  2223.    
  2224.    return (true);
  2225. }
  2226.  
  2227.  
  2228.  
  2229. /***********************************************************************
  2230.  *
  2231.  * FUNCTION:    NoteViewDoCommand
  2232.  *
  2233.  * DESCRIPTION: This routine performs the menu command specified.
  2234.  *
  2235.  * PARAMETERS:  command  - menu item id
  2236.  *
  2237.  * RETURNED:    nothing
  2238.  *
  2239.  * REVISION HISTORY:
  2240.  *         Name   Date      Description
  2241.  *         ----   ----      -----------
  2242.  *         art   6/5/95      Initial Revision
  2243.  *
  2244.  ***********************************************************************/
  2245. static Boolean NoteViewDoCommand (Word command)
  2246. {
  2247.    FieldPtr fld;
  2248.    Boolean handled = true;
  2249.    
  2250.    switch (command)
  2251.       {
  2252.         case noteFontCmd:
  2253.             NoteFont = SelectFont (NoteFont);
  2254.             break; 
  2255.  
  2256.       case noteTopOfPageCmd:
  2257.          fld = GetObjectPtr (NoteField);
  2258.          FldSetScrollPosition (fld, 0);
  2259.          NoteViewUpdateScrollBar ();
  2260.          break;
  2261.  
  2262.       case noteBottomOfPageCmd:
  2263.          fld = GetObjectPtr (NoteField);
  2264.          FldSetScrollPosition (fld, FldGetTextLength (fld));
  2265.          NoteViewUpdateScrollBar ();
  2266.          break;
  2267.  
  2268.         case notePhoneLookupCmd:
  2269.             fld = GetObjectPtr (NoteField);
  2270.             PhoneNumberLookup (fld);
  2271.             break;
  2272.             
  2273.       default:
  2274.          handled = false;
  2275.       }   
  2276.    return (handled);
  2277. }
  2278.  
  2279.  
  2280. /***********************************************************************
  2281.  *
  2282.  * FUNCTION:    NoteViewScroll
  2283.  *
  2284.  * DESCRIPTION: This routine scrolls the mote Note View a page or a 
  2285.  *              line at a time.
  2286.  *
  2287.  * PARAMETERS:  direction - up or dowm
  2288.  *              oneLine   - true if scrolling a single line
  2289.  *
  2290.  * RETURNED:    nothing
  2291.  *
  2292.  * REVISION HISTORY:
  2293.  *         Name   Date      Description
  2294.  *         ----   ----      -----------
  2295.  *         art   7/1/96   Initial Revision
  2296.  *
  2297.  ***********************************************************************/
  2298. static void NoteViewScroll (Short linesToScroll)
  2299. {
  2300.    Word            blankLines;
  2301.    Short            min;
  2302.    Short            max;
  2303.    Short            value;
  2304.    Short            pageSize;
  2305.    FieldPtr         fld;
  2306.    ScrollBarPtr   bar;
  2307.    
  2308.    fld = GetObjectPtr (NoteField);
  2309.  
  2310.    if (linesToScroll < 0)
  2311.       {
  2312.       blankLines = FldGetNumberOfBlankLines (fld);
  2313.       FldScrollField (fld, -linesToScroll, up);
  2314.       
  2315.       // If there were blank lines visible at the end of the field
  2316.       // then we need to update the scroll bar.
  2317.       if (blankLines)
  2318.          {
  2319.          // Update the scroll bar.
  2320.          bar = GetObjectPtr (NoteScrollBar);
  2321.          SclGetScrollBar (bar, &value, &min, &max, &pageSize);
  2322.          if (blankLines > -linesToScroll)
  2323.             max += linesToScroll;
  2324.          else
  2325.             max -= blankLines;
  2326.          SclSetScrollBar (bar, value, min, max, pageSize);
  2327.          }
  2328.       }
  2329.  
  2330.    else if (linesToScroll > 0)
  2331.       FldScrollField (fld, linesToScroll, down);
  2332. }
  2333.  
  2334.  
  2335. /***********************************************************************
  2336.  *
  2337.  * FUNCTION:    NoteViewPageScroll
  2338.  *
  2339.  * DESCRIPTION: This routine scrolls the message a page up or down.
  2340.  *
  2341.  * PARAMETERS:   direction     up or down
  2342.  *
  2343.  * RETURNED:    nothing
  2344.  *
  2345.  * REVISION HISTORY:
  2346.  *         Name   Date      Description
  2347.  *         ----   ----      -----------
  2348.  *         art   7/1/96   Initial Revision
  2349.  *
  2350.  ***********************************************************************/
  2351. static void NoteViewPageScroll (DirectionType direction)
  2352. {
  2353.    Short value;
  2354.    Short min;
  2355.    Short max;
  2356.    Short pageSize;
  2357.    Word linesToScroll;
  2358.    FieldPtr fld;
  2359.    ScrollBarPtr bar;
  2360.  
  2361.    fld = GetObjectPtr (NoteField);
  2362.    
  2363.    if (FldScrollable (fld, direction))
  2364.       {
  2365.       linesToScroll = FldGetVisibleLines (fld) - 1;
  2366.       FldScrollField (fld, linesToScroll, direction);
  2367.  
  2368.       // Update the scroll bar.
  2369.       bar = GetObjectPtr (NoteScrollBar);
  2370.       SclGetScrollBar (bar, &value, &min, &max, &pageSize);
  2371.  
  2372.       if (direction == up)
  2373.          value -= linesToScroll;
  2374.       else
  2375.          value += linesToScroll;
  2376.       
  2377.       SclSetScrollBar (bar, value, min, max, pageSize);
  2378.       return;
  2379.       }
  2380. }
  2381.  
  2382.  
  2383. /***********************************************************************
  2384.  *
  2385.  * FUNCTION:    NoteViewInit
  2386.  *
  2387.  * DESCRIPTION: This routine initials the Edit View form.
  2388.  *
  2389.  * PARAMETERS:  frm - pointer to the Edit View form.
  2390.  *
  2391.  * RETURNED:    nothing
  2392.  *
  2393.  * REVISION HISTORY:
  2394.  *         Name   Date      Description
  2395.  *         ----   ----      -----------
  2396.  *         art   6/5/95   Initial Revision
  2397.  *
  2398.  ***********************************************************************/
  2399. static void NoteViewInit (FormPtr frm)
  2400. {
  2401.    FieldPtr       fld;
  2402.    FieldAttrType   attr;
  2403.    
  2404.    NoteViewLoadRecord ();
  2405.    
  2406.  
  2407.     // Hide the font controls that are used in version 1.0 and 2.0 .
  2408.     FrmHideObject (frm, FrmGetObjectIndex (frm, NoteSmallFontButton));
  2409.     FrmHideObject (frm, FrmGetObjectIndex (frm, NoteLargeFontButton));
  2410.  
  2411.  
  2412.    // Have the field send events to maintain the scroll bar.
  2413.    fld = GetObjectPtr (NoteField);
  2414.    FldGetAttributes (fld, &attr);
  2415.    attr.hasScrollBar = true;
  2416.    FldSetAttributes (fld, &attr);
  2417. }
  2418.  
  2419.  
  2420. /***********************************************************************
  2421.  *
  2422.  * FUNCTION:    NoteViewHandleEvent
  2423.  *
  2424.  * DESCRIPTION: This routine is the event handler for the "Edit View"
  2425.  *              of the ToDo application.
  2426.  *
  2427.  * PARAMETERS:  event  - a pointer to an EventType structure
  2428.  *
  2429.  * RETURNED:    true if the event has handled and should not be passed
  2430.  *              to a higher level handler.
  2431.  *
  2432.  * REVISION HISTORY:
  2433.  *         Name   Date      Description
  2434.  *         ----   ----      -----------
  2435.  *         art   6/5/95   Initial Revision
  2436.  *
  2437.  ***********************************************************************/
  2438. static Boolean NoteViewHandleEvent (EventPtr event)
  2439. {
  2440.    FormPtr frm;
  2441.    Boolean handled = false;
  2442.    FieldPtr fldP;
  2443.  
  2444.    
  2445.    switch (event->eType)
  2446.        {
  2447.       case keyDownEvent:
  2448.          if (TxtCharIsHardKey(event->data.keyDown.modifiers, event->data.keyDown.chr))
  2449.              {
  2450.             NoteViewSave ();
  2451.             FrmGotoForm (ListView);
  2452.             handled = true;
  2453.              } 
  2454.          else if (event->data.keyDown.chr == pageUpChr)
  2455.              {
  2456.             NoteViewPageScroll (up);
  2457.             handled = true;
  2458.              } 
  2459.          else if (event->data.keyDown.chr == pageDownChr) 
  2460.                 {
  2461.             NoteViewPageScroll (down);
  2462.             handled = true;
  2463.              }
  2464.          break;
  2465.  
  2466.  
  2467.       case ctlSelectEvent:
  2468.          switch (event->data.ctlSelect.controlID)
  2469.              {
  2470.             case NoteDoneButton:
  2471.                NoteViewSave ();
  2472.                
  2473.                // When we return to the ListView highlight this record.
  2474.                if (PriorAddressFormID == ListView)
  2475.                   ListViewSelectThisRecord = CurrentRecord;
  2476.                
  2477.                FrmGotoForm(PriorAddressFormID);
  2478.                handled = true;
  2479.                break;
  2480.  
  2481.             case NoteDeleteButton:
  2482.                if (NoteViewDeleteNote ())
  2483.                   FrmGotoForm (PriorAddressFormID);
  2484.                
  2485.                ListViewSelectThisRecord = noRecord;
  2486.                handled = true;
  2487.                break;
  2488.                
  2489.             default:
  2490.                break;
  2491.              }
  2492.          break;
  2493.  
  2494.       case fldChangedEvent:
  2495.          frm = FrmGetActiveForm ();
  2496.          NoteViewUpdateScrollBar ();
  2497.          handled = true;
  2498.          break;
  2499.       
  2500.       case menuEvent:
  2501.          return NoteViewDoCommand (event->data.menu.itemID);
  2502.       
  2503.       case frmOpenEvent:
  2504.          frm = FrmGetActiveForm ();
  2505.          NoteViewInit (frm);
  2506.          NoteViewDrawTitle ();
  2507.          FrmDrawForm (frm);
  2508.          NoteViewUpdateScrollBar ();
  2509.          FrmSetFocus (frm, FrmGetObjectIndex (frm, NoteField));
  2510.          handled = true;
  2511.          break;
  2512.  
  2513.       case frmGotoEvent:
  2514.          frm = FrmGetActiveForm ();
  2515.          CurrentRecord = event->data.frmGoto.recordNum;
  2516.          NoteViewInit (frm);
  2517.          fldP = GetObjectPtr (NoteField);
  2518.          FldSetScrollPosition(fldP, event->data.frmGoto.matchPos);
  2519.          FldSetSelection(fldP, event->data.frmGoto.matchPos, 
  2520.                event->data.frmGoto.matchPos + event->data.frmGoto.matchLen);
  2521.          NoteViewDrawTitle ();
  2522.          FrmDrawForm (frm);
  2523.          NoteViewUpdateScrollBar ();
  2524.          FrmSetFocus (frm, FrmGetObjectIndex (frm, NoteField));
  2525.          handled = true;
  2526.          break;
  2527.       
  2528.  
  2529.       case frmUpdateEvent:
  2530.         if (event->data.frmUpdate.updateCode & updateFontChanged)
  2531.             {
  2532.             fldP = GetObjectPtr (NoteField);
  2533.             FldSetFont (fldP, NoteFont);
  2534.             NoteViewUpdateScrollBar ();
  2535.             handled = true;
  2536.             }
  2537.         else
  2538.             {
  2539.             frm = FrmGetActiveForm ();
  2540.             NoteViewDrawTitle ();
  2541.             FrmDrawForm (frm);
  2542.             handled = true;
  2543.             }
  2544.         break;
  2545.  
  2546.       case frmCloseEvent:
  2547.          if (UnnamedRecordStringPtr)
  2548.              {
  2549.             MemPtrUnlock(UnnamedRecordStringPtr);
  2550.             UnnamedRecordStringPtr = NULL;
  2551.              }
  2552.          if ( FldGetTextHandle (GetObjectPtr (NoteField)))
  2553.             NoteViewSave ();
  2554.          break;
  2555.  
  2556.  
  2557.       case sclRepeatEvent:
  2558.          NoteViewScroll (event->data.sclRepeat.newValue - 
  2559.             event->data.sclRepeat.value);
  2560.          break;
  2561.       
  2562.       default:
  2563.           break;
  2564.        }
  2565.  
  2566.    return (handled);
  2567. }
  2568.  
  2569.  
  2570. #pragma mark ----------------
  2571. /***********************************************************************
  2572.  *
  2573.  * FUNCTION:    EditViewRestoreEditState
  2574.  *
  2575.  * DESCRIPTION: This routine restores the edit state of the Edit
  2576.  *
  2577.  * PARAMETERS:  nothing
  2578.  *
  2579.  * RETURNED:    nothing
  2580.  *
  2581.  * REVISION HISTORY:
  2582.  *            Name    Date        Description
  2583.  *            ----    ----        -----------
  2584.  *            art    9/12/97    Initial Revision
  2585.  *
  2586.  ***********************************************************************/
  2587. static void EditViewRestoreEditState ()
  2588. {
  2589.     Word            row;
  2590.     FormPtr        frm;
  2591.     TablePtr        table;
  2592.     FieldPtr        fld;
  2593.  
  2594.     if (CurrentFieldIndex == noFieldIndex) return;
  2595.  
  2596.     // Find the row that the current field is in.
  2597.     table = GetObjectPtr (EditTable);
  2598.     if ( ! TblFindRowID (table, CurrentFieldIndex, &row) )
  2599.         return;
  2600.  
  2601.     frm = FrmGetActiveForm ();
  2602.     FrmSetFocus (frm, FrmGetObjectIndex (frm, EditTable));
  2603.     TblGrabFocus (table, row, editDataColumn);
  2604.     
  2605.     // Restore the insertion point position.
  2606.     fld = TblGetCurrentField (table);
  2607.     FldSetInsPtPosition (fld, EditFieldPosition);
  2608.     FldGrabFocus (fld);
  2609. }
  2610.  
  2611.  
  2612. /***********************************************************************
  2613.  *
  2614.  * FUNCTION:    EditSetGraffitiMode
  2615.  *
  2616.  * DESCRIPTION: Set the graffity mode based on the field being edited.
  2617.  *
  2618.  * PARAMETERS:  currentField - the field being edited.
  2619.  *
  2620.  * RETURNED:    the graffiti mode is set
  2621.  *
  2622.  * REVISION HISTORY:
  2623.  *         Name   Date      Description
  2624.  *         ----   ----      -----------
  2625.  *         roger   9/20/95   Initial Revision
  2626.  *
  2627.  ***********************************************************************/
  2628. static void EditSetGraffitiMode (FieldPtr fld, UInt currentField)
  2629. {
  2630.    Handle currentRecordH;
  2631.    Boolean autoShift;
  2632.    FieldAttrType attr;
  2633.    AddrDBRecordType currentRecord;
  2634.  
  2635.  
  2636.    AddrGetRecord(AddrDB, CurrentRecord, ¤tRecord, ¤tRecordH);
  2637.  
  2638.    if (! isPhoneField(currentField))
  2639.       {
  2640.       // Set the field to support auto-shift.
  2641.       autoShift = true;
  2642.       }
  2643.    else
  2644.       {
  2645.       GrfSetState(false, true, false);
  2646.       autoShift = false;
  2647.       }
  2648.       
  2649.    if (fld)
  2650.       {
  2651.       FldGetAttributes (fld, &attr);
  2652.       attr.autoShift = autoShift;
  2653.       FldSetAttributes (fld, &attr);
  2654.       }
  2655.  
  2656.  
  2657.    MemHandleUnlock(currentRecordH);
  2658. }
  2659.  
  2660.  
  2661. /***********************************************************************
  2662.  *
  2663.  * FUNCTION:    EditViewGetRecordField
  2664.  *
  2665.  * DESCRIPTION: This routine returns a pointer to a field of the 
  2666.  *              address record.  This routine is called by the table 
  2667.  *              object as a callback routine when it wants to display or
  2668.  *              edit a field.
  2669.  *
  2670.  * PARAMETERS:  table  - pointer to the memo list table (TablePtr)
  2671.  *              row    - row of the table to draw
  2672.  *              column - column of the table to draw 
  2673.  *
  2674.  * RETURNED:    nothing
  2675.  *
  2676.  * REVISION HISTORY:
  2677.  *         Name   Date      Description
  2678.  *         ----   ----      -----------
  2679.  *         art   6/6/95      Initial Revision
  2680.  *
  2681.  ***********************************************************************/
  2682. static Err EditViewGetRecordField (VoidPtr table, Word row, Word column, 
  2683.    Boolean editing, VoidHand * textH, WordPtr textOffset, WordPtr textAllocSize, 
  2684.    FieldPtr fld)
  2685. {
  2686.    Word fieldNum;
  2687.    Word  fieldIndex;
  2688.    CharPtr recordP, fieldP;
  2689.    VoidHand recordH, fieldH;
  2690.    UInt fieldSize;
  2691.    AddrDBRecordType record;
  2692.  
  2693.    
  2694.    // Get the field number that corresponds to the table item.
  2695.    // The field number is stored as the row id.
  2696.    //
  2697.    fieldIndex = TblGetRowID (table, row);
  2698.    fieldNum = FieldMap[fieldIndex];
  2699.  
  2700.    AddrGetRecord (AddrDB, CurrentRecord, &record, &recordH);
  2701.  
  2702.    if (editing)
  2703.       {
  2704.       EditSetGraffitiMode(fld, fieldNum);
  2705.       if (record.fields[fieldNum])
  2706.          {
  2707.          fieldSize = StrLen(record.fields[fieldNum]) + 1;
  2708.          fieldH = MemHandleNew(fieldSize);
  2709.          fieldP = MemHandleLock(fieldH);
  2710.          MemMove(fieldP, record.fields[fieldNum], fieldSize);
  2711.          *textAllocSize = fieldSize;
  2712.          MemHandleUnlock(fieldH);
  2713.          }
  2714.       else
  2715.          {
  2716.          fieldH = 0;
  2717.          *textAllocSize = 0;
  2718.          }
  2719.       MemHandleUnlock (recordH);
  2720.       *textOffset = 0;         // only one string
  2721.       *textH = fieldH;
  2722.       return (0);
  2723.       
  2724.       }
  2725.    else
  2726.       {
  2727.       // Calculate the offset from the start of the record.
  2728.       recordP = MemHandleLock (recordH);   // record now locked twice
  2729.  
  2730.       if (record.fields[fieldNum])
  2731.          {
  2732.          *textOffset = record.fields[fieldNum] - recordP;
  2733.          *textAllocSize = StrLen (record.fields[fieldNum]) + 1;  // one for null terminator
  2734.          }
  2735.       else
  2736.          {
  2737.          do
  2738.             {
  2739.             fieldNum++;
  2740.             } while (fieldNum < addressFieldsCount &&
  2741.                record.fields[fieldNum] == NULL);
  2742.       
  2743.          if (fieldNum < addressFieldsCount)
  2744.             *textOffset = record.fields[fieldNum] - recordP;
  2745.          else
  2746.             // Place the new field at the end of the text.
  2747.             *textOffset = MemHandleSize(recordH);   
  2748.  
  2749.          *textAllocSize = 0;  // one for null terminator
  2750.          }
  2751.       MemHandleUnlock (recordH);   // unlock the second lock
  2752.  
  2753.       }
  2754.  
  2755.    MemHandleUnlock (recordH);      // unlock the AddrGetRecord lock
  2756.  
  2757.    *textH = recordH;
  2758.    return (0);
  2759. }
  2760.  
  2761.  
  2762. /***********************************************************************
  2763.  *
  2764.  * FUNCTION:    EditViewSaveRecordField
  2765.  *
  2766.  * DESCRIPTION: This routine saves a field of an address to the 
  2767.  *              database.  This routine is called by the table 
  2768.  *              object, as a callback routine, when it wants to save
  2769.  *              an item.
  2770.  *
  2771.  * PARAMETERS:  table  - pointer to the memo list table (TablePtr)
  2772.  *              row    - row of the table to draw
  2773.  *              column - column of the table to draw 
  2774.  *
  2775.  * RETURNED:    true if the table needs to be redrawn
  2776.  *
  2777.  * REVISION HISTORY:
  2778.  *         Name   Date      Description
  2779.  *         ----   ----      -----------
  2780.  *         art   2/21/95   Initial Revision
  2781.  *
  2782.  ***********************************************************************/
  2783. static Boolean EditViewSaveRecordField (VoidPtr table, Word row, Word column)
  2784. {
  2785.    Word fieldNum;
  2786.    Word fieldIndex;
  2787.    FieldPtr fld;
  2788.    AddrDBRecordType record;
  2789.    Handle recordH;
  2790.    VoidHand textH;
  2791.    CharPtr textP;
  2792.    AddrDBRecordFlags bit;
  2793.    UInt i;
  2794.    Err err;
  2795.    Boolean redraw = false;
  2796.    UInt numOfRows;
  2797.    Int newSize;
  2798.    
  2799.    
  2800.    fld = TblGetCurrentField (table);
  2801.    textH = FldGetTextHandle(fld);
  2802.    
  2803.    // Get the field number that corresponds to the table item to save.
  2804.    fieldIndex = TblGetRowID (table, row);
  2805.    fieldNum = FieldMap[fieldIndex];
  2806.    
  2807.    // Save the field last edited.
  2808.    EditRowIDWhichHadFocus = fieldIndex;
  2809.  
  2810.    // Save the cursor position of the field last edited.
  2811.    // Check if the top of the text is scroll off the top of the 
  2812.    // field, if it is then redraw the field.
  2813.    if (FldGetScrollPosition (fld))
  2814.       {
  2815.       FldSetScrollPosition (fld, 0);
  2816.       EditFieldPosition = 0;
  2817.       }
  2818.    else
  2819.       EditFieldPosition = FldGetInsPtPosition (fld);
  2820.  
  2821.    // Make sure there any selection is removed since we will free
  2822.    // the text memory before the callee can remove the selection.
  2823.    FldSetSelection (fld, 0, 0);
  2824.  
  2825.  
  2826.    if (FldDirty (fld))
  2827.       {
  2828.       DirtyRecord (CurrentRecord);
  2829.       
  2830.       if (textH == 0)
  2831.          textP = NULL;
  2832.       else
  2833.          {
  2834.          textP = MemHandleLock(textH);
  2835.          if (textP[0] == '\0')
  2836.             textP = NULL;
  2837.          }
  2838.          
  2839.       
  2840.       AddrGetRecord (AddrDB, CurrentRecord, &record, &recordH);
  2841.       record.fields[fieldNum] = textP;
  2842.       
  2843.       // If we have changed a phone field and if the show if
  2844.       // list view phone is blank set it to the first non blank phone
  2845.       // This rule should allow:
  2846.       // 1. Showing a blank field is possible
  2847.       // 2. Deleting the shown field switches to another
  2848.       // 3. Adding a field when there isn't one shows it.
  2849.       if (isPhoneField(fieldNum) &&
  2850.          record.fields[firstPhoneField + record.options.phones.displayPhoneForList] == NULL)
  2851.          {
  2852.          for (i = firstPhoneField; i <= lastPhoneField; i++)
  2853.             {
  2854.             if (record.fields[i] != NULL)
  2855.                {
  2856.                record.options.phones.displayPhoneForList = i - firstPhoneField;
  2857.                break;
  2858.                }
  2859.             }
  2860.          }
  2861.                
  2862.       
  2863.       bit.allBits = (ULong)1 << fieldNum;
  2864.       err = AddrChangeRecord(AddrDB, &CurrentRecord, &record, bit);
  2865.  
  2866.       // The new field has been copied into the new record.  Unlock it.
  2867.       if (textP)
  2868.          MemPtrUnlock(textP);
  2869.  
  2870.       // The change was not made (probably storage out of memory)      
  2871.       if (err)
  2872.          {
  2873.          // Because the storage is full the text in the text field differs
  2874.          // from the text in the record.  EditViewGetFieldHeight uses
  2875.          // the text in the field (because it's being edited).
  2876.          // Make the text in the field the same as the text in the record.
  2877.          // Resizing should always be possible.
  2878.          MemHandleUnlock(recordH);      // Get original text
  2879.          AddrGetRecord (AddrDB, CurrentRecord, &record, &recordH);
  2880.          
  2881.          if (record.fields[fieldNum] == NULL)
  2882.             newSize = 1;
  2883.          else
  2884.             newSize = StrLen(record.fields[fieldNum]) + 1;
  2885.             
  2886.          if (!MemHandleResize(textH, newSize))
  2887.             {
  2888.             textP = MemHandleLock(textH);
  2889.             if (newSize > 1)
  2890.                StrCopy(textP, record.fields[fieldNum]);
  2891.             else
  2892.                textP[0] = '\0';
  2893.             MemPtrUnlock(textP);
  2894.             
  2895.             // Update the text field
  2896.             FldSetTextHandle (fld, 0);
  2897.             FldSetTextHandle (fld, textH);
  2898.             }
  2899.          else
  2900.             {
  2901.             ErrFatalDisplayIf(true, "Resize failed.");
  2902.             }
  2903.          
  2904.          
  2905.          MemHandleUnlock(recordH);
  2906.          FrmAlert(DeviceFullAlert);
  2907.          
  2908.          // The field may no longer be the same height.  This row and those
  2909.          // below may need to be recalced. Mark this row and those
  2910.          // below it not usable and reload the table.
  2911.          numOfRows = TblGetNumberOfRows(table);
  2912.          while (row < numOfRows)
  2913.             {
  2914.             TblSetRowUsable(table, row, false);
  2915.             row++;
  2916.             }
  2917.          EditViewLoadTable();
  2918.          redraw = true;                  // redraw the table showing change lost
  2919.          }
  2920.  
  2921.       }
  2922.  
  2923.    // Free the memory used for the field's text because the table suppresses it.
  2924.    FldFreeMemory (fld);
  2925.  
  2926.  
  2927.    return redraw;
  2928. }
  2929.  
  2930.  
  2931. /***********************************************************************
  2932.  *
  2933.  * FUNCTION:    EditViewSaveRecord
  2934.  *
  2935.  * DESCRIPTION: Checks the record and saves it if it's OK
  2936.  *
  2937.  * PARAMETERS:  event  - a pointer to an EventType structure
  2938.  *
  2939.  * RETURNED:    The view that should be switched to.
  2940.  *
  2941.  * REVISION HISTORY:
  2942.  *         Name   Date      Description
  2943.  *         ----   ----      -----------
  2944.  *         rsf   9/20/95   Initial Revision
  2945.  *
  2946.  ***********************************************************************/
  2947. static UInt EditViewSaveRecord ()
  2948. {
  2949.    Handle currentRecordH;
  2950.    AddrDBRecordType currentRecord;
  2951.    FormPtr frm;
  2952.    TablePtr tableP;
  2953.    Boolean hasData;
  2954.  
  2955.  
  2956.    // Make sure the field being edited is saved
  2957.    frm = FrmGetActiveForm ();
  2958.    tableP = FrmGetObjectPtr (frm, FrmGetObjectIndex(frm, EditTable));
  2959.    TblReleaseFocus(tableP);
  2960.    
  2961.    
  2962.    // If this record is needed then leave.  This is a good time because
  2963.    // the data is saved and this is before the record could be deleted.
  2964.    if (RecordNeededAfterEditView)
  2965.       {
  2966.       ListViewSelectThisRecord = noRecord;
  2967.       return ListView;
  2968.       }
  2969.    
  2970.    
  2971.    // The record may have already been delete by the Delete menu command
  2972.    // or the details dialog.  If there isn't a CurrentRecord assume the
  2973.    // record has been deleted.
  2974.    if (CurrentRecord == noRecord)
  2975.       {
  2976.       ListViewSelectThisRecord = noRecord;
  2977.       return ListView;
  2978.       }
  2979.    
  2980.    // If there is no data then then delete the record.
  2981.    // If there is data but no name data then demand some.
  2982.    
  2983.    AddrGetRecord(AddrDB, CurrentRecord, ¤tRecord, ¤tRecordH);
  2984.    
  2985.    hasData = RecordContainsData(¤tRecord);
  2986.    
  2987.    // Unlock before the DeleteRecord.   We can only rely on
  2988.    // NULL pointers from here on out.   
  2989.    MemHandleUnlock(currentRecordH);
  2990.  
  2991.  
  2992.    // If none are the fields contained anything then 
  2993.    // delete the field.
  2994.    if (!hasData)
  2995.       {
  2996.       DeleteRecord(false);   // uniq ID wasted?  Yes. We don't care.
  2997.       return ListView;
  2998.       }
  2999.  
  3000.     
  3001.    // The record's category may have been changed.  The CurrentCategory
  3002.    // isn't supposed to change in this case.  Make sure the CurrentRecord
  3003.    // is still visible in this category or pick another one near it.
  3004.    if (!SeekRecord(&CurrentRecord, 0, dmSeekBackward))
  3005.       if (!SeekRecord(&CurrentRecord, 0, dmSeekForward))
  3006.          CurrentRecord = noRecord;
  3007.    
  3008.    
  3009.    ListViewSelectThisRecord = CurrentRecord;
  3010.  
  3011.    return ListView;
  3012. }
  3013.  
  3014.  
  3015. /***********************************************************************
  3016.  *
  3017.  * FUNCTION:    EditViewSelectCategory
  3018.  *
  3019.  * DESCRIPTION: This routine handles selection, creation and deletion of
  3020.  *              categories from the "Edit View".  
  3021.  *
  3022.  * PARAMETERS:  nothing
  3023.  *
  3024.  * RETURNED:    The index of the new category.
  3025.  *
  3026.  *              The following global variables are modified:
  3027.  *                     CurrentCategory
  3028.  *                     ShowAllCategories
  3029.  *                     CategoryName
  3030.  *
  3031.  * REVISION HISTORY:
  3032.  *         Name   Date      Description
  3033.  *         ----   ----      -----------
  3034.  *         art   6/5/95   Initial Revision
  3035.  *
  3036.  ***********************************************************************/
  3037. static void EditViewSelectCategory (void)
  3038. {
  3039.    UInt attr;
  3040.    FormPtr frm;
  3041.    Word category;
  3042.    Boolean categoryEdited;
  3043.  
  3044.    
  3045.    // Process the category popup list.
  3046.    DmRecordInfo (AddrDB, CurrentRecord, &attr, NULL, NULL);
  3047.    category = attr & dmRecAttrCategoryMask;
  3048.    
  3049.    frm = FrmGetActiveForm();
  3050.    categoryEdited = CategorySelect (AddrDB, frm, EditCategoryTrigger,
  3051.                EditCategoryList, false, &category, CategoryName, 1, 0);
  3052.    
  3053.    if (categoryEdited || (category != (attr & dmRecAttrCategoryMask)))
  3054.       {
  3055.       // Change the category of the record.
  3056.       DmRecordInfo (AddrDB, CurrentRecord, &attr, NULL, NULL);   
  3057.       attr &= ~dmRecAttrCategoryMask;
  3058.       attr |= category | dmRecAttrDirty;
  3059.       DmSetRecordInfo (AddrDB, CurrentRecord, &attr, NULL);
  3060.  
  3061.       ChangeCategory (category);
  3062.       }
  3063. }
  3064.  
  3065. /***********************************************************************
  3066.  *
  3067.  * FUNCTION:    EditViewUpdateScrollers
  3068.  *
  3069.  * DESCRIPTION: This routine draws or erases the edit view scroll arrow
  3070.  *              buttons.
  3071.  *
  3072.  * PARAMETERS:  frm             -  pointer to the address edit form
  3073.  *              bottomField     -  field index of the last visible row
  3074.  *              lastItemClipped - true if the last visible row is clip at 
  3075.  *                                 the bottom
  3076.  *
  3077.  * RETURNED:    nothing
  3078.  *
  3079.  * REVISION HISTORY:
  3080.  *         Name   Date      Description
  3081.  *         ----   ----      -----------
  3082.  *         roger   6/26/95   Initial Revision
  3083.  *
  3084.  ***********************************************************************/
  3085. static void EditViewUpdateScrollers (FormPtr frm, Word bottomFieldIndex,
  3086.    Boolean lastItemClipped)
  3087. {
  3088.    Word upIndex;
  3089.    Word downIndex;
  3090.    Boolean scrollableUp;
  3091.    Boolean scrollableDown;
  3092.       
  3093.    // If the first field displayed is not the fist field in the record,
  3094.    // enable the up scroller.
  3095.    scrollableUp = TopVisibleFieldIndex > 0;
  3096.  
  3097.    // If the last field displayed is not the last field in the record,
  3098.    // enable the down scroller.
  3099.    scrollableDown = (lastItemClipped || (bottomFieldIndex < editLastFieldIndex));
  3100.  
  3101.  
  3102.    // Update the scroll button.
  3103.    upIndex = FrmGetObjectIndex (frm, EditUpButton);
  3104.    downIndex = FrmGetObjectIndex (frm, EditDownButton);
  3105.    FrmUpdateScrollers (frm, upIndex, downIndex, scrollableUp, scrollableDown);
  3106. }
  3107.  
  3108.  
  3109. /***********************************************************************
  3110.  *
  3111.  * FUNCTION:    EditViewGetFieldHeight
  3112.  *
  3113.  * DESCRIPTION: This routine initialize a row in the to do list.
  3114.  *
  3115.  * PARAMETERS:  table        - pointer to the table of to do items
  3116.  *              fieldIndex   - the index of the field displayed in the row
  3117.  *              columnWidth  - height of the row in pixels
  3118.  *
  3119.  * RETURNED:    height of the field in pixels
  3120.  *
  3121.  * REVISION HISTORY:
  3122.  *            Name    Date        Description
  3123.  *            ----    ----        -----------
  3124.  *            art    6/26/95    Initial Revision
  3125.  *            art    9/11/97    Add font support.
  3126.  *
  3127.  ***********************************************************************/
  3128. static Word EditViewGetFieldHeight (TablePtr table, Word fieldIndex, Word columnWidth,
  3129.     Word maxHeight, AddrDBRecordPtr record, FontID * fontIdP)
  3130. {
  3131.     Word row;
  3132.     Word column;
  3133.     Word index;
  3134.     Word height;
  3135.     Word lineHeight;
  3136.     FontID currFont;
  3137.     CharPtr str;
  3138.     FieldPtr fld;
  3139.  
  3140.     if (TblEditing (table))
  3141.         {
  3142.         TblGetSelection (table, &row, &column);
  3143.         if (fieldIndex == TblGetRowID (table, row))
  3144.             {
  3145.             fld = TblGetCurrentField (table);
  3146.             str = FldGetTextPtr (fld);
  3147.             }
  3148.         else
  3149.             {
  3150.             index = FieldMap[fieldIndex];
  3151.             str = record->fields[index];
  3152.             }
  3153.         }
  3154.     else
  3155.         {
  3156.         index = FieldMap[fieldIndex];
  3157.         str = record->fields[index];
  3158.         }
  3159.  
  3160.  
  3161.     // If the field has text empty, or the field is the current field, or
  3162.     // the font used to display blank lines is the same as the font used
  3163.     // to display text then used the view's current font setting. 
  3164.     if ( (str && *str) || 
  3165.           (CurrentFieldIndex == fieldIndex) ||
  3166.           (AddrEditFont == addrEditBlankFont))
  3167.         {
  3168.         *fontIdP = AddrEditFont;
  3169.         currFont = FntSetFont (*fontIdP);
  3170.         }
  3171.  
  3172.     // If the height of the font used to display blank lines is the same 
  3173.     // height as the font used to display text then used the view's 
  3174.     // current font setting.
  3175.     else
  3176.         {
  3177.         currFont = FntSetFont (addrEditBlankFont);
  3178.         lineHeight = FntLineHeight ();
  3179.         
  3180.         FntSetFont (AddrEditFont);
  3181.         if (lineHeight == FntLineHeight ())
  3182.             *fontIdP = AddrEditFont;
  3183.         else
  3184.             {
  3185.             *fontIdP = addrEditBlankFont;
  3186.             FntSetFont (addrEditBlankFont);
  3187.             }
  3188.         }
  3189.         
  3190.     height = FldCalcFieldHeight (str, columnWidth);
  3191.     lineHeight = FntLineHeight ();
  3192.     height = min (height, (maxHeight / lineHeight));
  3193.     height *= lineHeight;
  3194.  
  3195.     FntSetFont (currFont);
  3196.         
  3197.         
  3198.     return (height);
  3199. }
  3200.  
  3201. /***********************************************************************
  3202.  *
  3203.  * FUNCTION:    EditInitTableRow
  3204.  *
  3205.  * DESCRIPTION: This routine initialize a row in the edit view.
  3206.  *
  3207.  * PARAMETERS:  table       - pointer to the table of to do items
  3208.  *              row         - row number (first row is zero)
  3209.  *              fieldIndex  - the index of the field displayed in the row
  3210.  *              rowHeight   - height of the row in pixels
  3211.  *
  3212.  * RETURNED:    nothing
  3213.  *
  3214.  * REVISION HISTORY:
  3215.  *         Name   Date      Description
  3216.  *         ----   ----      -----------
  3217.  *         art   6/26/95      Initial Revision
  3218.  *
  3219.  ***********************************************************************/
  3220. static void EditInitTableRow (TablePtr table, Word row, Word fieldIndex, 
  3221.     short rowHeight, FontID fontID, AddrDBRecordPtr record, AddrAppInfoPtr appInfoPtr)
  3222. {
  3223.  
  3224.    // Make the row usable.
  3225.    TblSetRowUsable (table, row, true);
  3226.    
  3227.    // Set the height of the row to the height of the desc
  3228.    TblSetRowHeight (table, row, rowHeight);
  3229.    
  3230.    // Store the record number as the row id.
  3231.    TblSetRowID (table, row, fieldIndex);
  3232.    
  3233.    // Mark the row invalid so that it will draw when we call the 
  3234.    // draw routine.
  3235.    TblMarkRowInvalid (table, row);
  3236.  
  3237.     // Set the text font.
  3238.     TblSetItemFont (table, row, editDataColumn, fontID);
  3239.  
  3240.    // The label is either a text label or a popup menu (of phones)
  3241.    if (! isPhoneField(FieldMap[fieldIndex]))
  3242.       {      
  3243.       TblSetItemStyle (table, row, editLabelColumn, labelTableItem);
  3244.       TblSetItemPtr (table, row, editLabelColumn, 
  3245.          appInfoPtr->fieldLabels[FieldMap[fieldIndex]]);
  3246.       }
  3247.    else
  3248.       {
  3249.       // The label is a popup list
  3250.       TblSetItemStyle (table, row, editLabelColumn, popupTriggerTableItem);
  3251.       TblSetItemInt (table, row, editLabelColumn, GetPhoneLabel(record, FieldMap[fieldIndex]));
  3252.       TblSetItemPtr (table, row, editLabelColumn, 
  3253.          GetObjectPtr (EditPhoneList));
  3254.       }      
  3255. }
  3256.  
  3257.  
  3258. /***********************************************************************
  3259.  *
  3260.  * FUNCTION:    EditViewLoadTable
  3261.  *
  3262.  * DESCRIPTION: This routine reloads to do database records into
  3263.  *              the edit view.  This routine is called when:
  3264.  *                 o A field height changes (Typed text wraps to the next line)
  3265.  *                 o Scrolling
  3266.  *                 o Advancing to the next field causes scrolling
  3267.  *                 o The focus moves to another field
  3268.  *                 o A custom label changes
  3269.  *                 o The form is first opened.
  3270.  *
  3271.  *                The row ID is an index into FieldMap.
  3272.  *
  3273.  * PARAMETERS:  startingRow - index of the first row to redisplay.
  3274.  *
  3275.  * RETURNED:    nothing
  3276.  *
  3277.  * REVISION HISTORY:
  3278.  *            Name    Date        Description
  3279.  *            ----    ----        -----------
  3280.  *            art    5/1/95    Initial Revision
  3281.  *            art    9/10/97    Rewrote to support user selectable fonts.
  3282.  *
  3283.  ***********************************************************************/
  3284. static void EditViewLoadTable (void)
  3285. {
  3286.     Word row;
  3287.     Word numRows;
  3288.     Word lineHeight;
  3289.     Word fieldIndex;
  3290.     Word lastFieldIndex;
  3291.     Word dataHeight;
  3292.     Word tableHeight;
  3293.     Word columnWidth;
  3294.     Word pos, oldPos;
  3295.     Word height, oldHeight;
  3296.     FontID fontID;
  3297.     FontID currFont;
  3298.     FormPtr frm;
  3299.     TablePtr table;
  3300.     Boolean rowUsable;
  3301.     Boolean rowsInserted = false;
  3302.     Boolean lastItemClipped;
  3303.     RectangleType r;
  3304.     AddrDBRecordType record;
  3305.     Handle recordH;
  3306.     AddrAppInfoPtr appInfoPtr;
  3307.  
  3308.     
  3309.     appInfoPtr = (AddrAppInfoPtr) AddrAppInfoGetPtr(AddrDB);
  3310.         
  3311.     frm = FrmGetActiveForm ();
  3312.     
  3313.     // Get the current record
  3314.     AddrGetRecord (AddrDB, CurrentRecord, &record, &recordH);
  3315.  
  3316.  
  3317.     // Get the height of the table and the width of the description
  3318.     // column.
  3319.     table = GetObjectPtr (EditTable);
  3320.     TblGetBounds (table, &r);
  3321.     tableHeight = r.extent.y;
  3322.     columnWidth = TblGetColumnWidth (table, editDataColumn);
  3323.  
  3324.     // If we currently have a selected record, make sure that it is not
  3325.     // above the first visible record.
  3326.     if (CurrentFieldIndex != noFieldIndex)
  3327.         {
  3328.         if (CurrentFieldIndex < TopVisibleFieldIndex)
  3329.             TopVisibleFieldIndex = CurrentFieldIndex;
  3330.         }
  3331.  
  3332.     row = 0;
  3333.     dataHeight = 0;
  3334.     oldPos = pos = 0;
  3335.     fieldIndex = TopVisibleFieldIndex;
  3336.     lastFieldIndex = fieldIndex;
  3337.  
  3338.     // Load records into the table.
  3339.     while (fieldIndex <= editLastFieldIndex)
  3340.         {        
  3341.         // Compute the height of the field's text string.
  3342.         height = EditViewGetFieldHeight (table, fieldIndex, columnWidth, tableHeight, &record, &fontID);
  3343.  
  3344.         // Is there enought room for at least one line of the the decription.
  3345.         currFont = FntSetFont (fontID);
  3346.         lineHeight = FntLineHeight ();
  3347.         FntSetFont (currFont);
  3348.         if (tableHeight >= dataHeight + lineHeight)
  3349.             {
  3350.             rowUsable = TblRowUsable (table, row);
  3351.  
  3352.             // Get the height of the current row.
  3353.             if (rowUsable)
  3354.                 oldHeight = TblGetRowHeight (table, row);
  3355.             else
  3356.                 oldHeight = 0;
  3357.  
  3358.             // If the field is not already being displayed in the current 
  3359.             // row, load the field into the table.
  3360.             if ((! rowUsable) ||
  3361.                  (TblGetRowID (table, row) != fieldIndex) ||
  3362.                  (TblGetItemFont (table, row, editDataColumn) != fontID))
  3363.                 {
  3364.                 EditInitTableRow (table, row, fieldIndex, height, fontID,
  3365.                     &record, appInfoPtr);
  3366.                 }
  3367.             
  3368.             // If the height or the position of the item has changed draw the item.
  3369.             else if (height != oldHeight)
  3370.                 {
  3371.                 TblSetRowHeight (table, row, height);
  3372.                 TblMarkRowInvalid (table, row);
  3373.                 }
  3374.             else if (pos != oldPos)
  3375.                 {
  3376.                 TblMarkRowInvalid (table, row);
  3377.                 }
  3378.  
  3379.             pos += height;
  3380.             oldPos += oldHeight;
  3381.             lastFieldIndex = fieldIndex;
  3382.             fieldIndex++;
  3383.             row++;
  3384.             }
  3385.  
  3386.         dataHeight += height;
  3387.  
  3388.  
  3389.         // Is the table full?
  3390.         if (dataHeight >= tableHeight)        
  3391.             {
  3392.             // If we have a currently selected field, make sure that it is
  3393.             // not below the last visible field.  If the currently selected 
  3394.             // field is the last visible record, make sure the whole field 
  3395.             // is visible.
  3396.             if (CurrentFieldIndex == noFieldIndex)
  3397.                 break;
  3398.  
  3399.             // Above last visible?
  3400.             else if  (CurrentFieldIndex < fieldIndex)
  3401.                 break;
  3402.  
  3403.             // Last visible?
  3404.             else if (fieldIndex == lastFieldIndex)
  3405.                 {
  3406.                 if ((fieldIndex == TopVisibleFieldIndex) || (dataHeight == tableHeight))
  3407.                     break;
  3408.                 }    
  3409.  
  3410.             // Remove the top item from the table and reload the table again.
  3411.             TopVisibleFieldIndex++;
  3412.             fieldIndex = TopVisibleFieldIndex;
  3413.  
  3414.  
  3415.             // Below last visible.
  3416. //            else
  3417. //                TopVisibleFieldIndex = CurrentFieldIndex;
  3418.                 
  3419.             row = 0;
  3420.             dataHeight = 0;
  3421.             oldPos = pos = 0;
  3422.             }
  3423.         }
  3424.  
  3425.  
  3426.     // Hide the item that don't have any data.
  3427.     numRows = TblGetNumberOfRows (table);
  3428.     while (row < numRows)
  3429.         {        
  3430.         TblSetRowUsable (table, row, false);
  3431.         row++;
  3432.         }
  3433.         
  3434.     // If the table is not full and the first visible field is 
  3435.     // not the first field    in the record, displays enough fields
  3436.     // to fill out the table by adding fields to the top of the table.
  3437.     while (dataHeight < tableHeight)
  3438.         {
  3439.         fieldIndex = TopVisibleFieldIndex;
  3440.         if (fieldIndex == 0) break;
  3441.         fieldIndex--;
  3442.  
  3443.         // Compute the height of the field.
  3444.         height = EditViewGetFieldHeight (table, fieldIndex, 
  3445.             columnWidth, tableHeight, &record, &fontID);
  3446.  
  3447.             
  3448.         // If adding the item to the table will overflow the height of
  3449.         // the table, don't add the item.
  3450.         if (dataHeight + height > tableHeight)
  3451.             break;
  3452.         
  3453.         // Insert a row before the first row.
  3454.         TblInsertRow (table, 0);
  3455.  
  3456.         EditInitTableRow (table, 0, fieldIndex, height, fontID, &record, appInfoPtr);
  3457.         
  3458.         TopVisibleFieldIndex = fieldIndex;
  3459.         
  3460.         rowsInserted = true;
  3461.  
  3462.         dataHeight += height;
  3463.         }
  3464.         
  3465.     // If rows were inserted to full out the page, invalidate the whole
  3466.     // table, it all needs to be redrawn.
  3467.     if (rowsInserted)
  3468.         TblMarkTableInvalid (table);
  3469.  
  3470.     // If the height of the data in the table is greater than the height
  3471.     // of the table, then the bottom of the last row is clip and the 
  3472.     // table is scrollable.
  3473.     lastItemClipped = (dataHeight > tableHeight);
  3474.  
  3475.     // Update the scroll arrows.
  3476.     EditViewUpdateScrollers (frm, lastFieldIndex, lastItemClipped);
  3477.  
  3478.  
  3479.     MemHandleUnlock(recordH);
  3480.     MemPtrUnlock(appInfoPtr);
  3481. }
  3482.  
  3483. /***********************************************************************
  3484.  *
  3485.  * FUNCTION:    EditViewDrawBusinessCardIndicator
  3486.  *
  3487.  * DESCRIPTION: Draw the business card indicator if the current record is
  3488.  * the business card.
  3489.  *
  3490.  * PARAMETERS:  formP - the form containing the business card indicator
  3491.  *
  3492.  * RETURNED:    nothing
  3493.  *
  3494.  * REVISION HISTORY:
  3495.  *         Name   Date      Description
  3496.  *         ----   ----      -----------
  3497.  *         roger  12/3/97  Initial Revision
  3498.  *
  3499.  ***********************************************************************/
  3500. static void EditViewDrawBusinessCardIndicator (FormPtr formP)
  3501. {
  3502.     DWord uniqueID;
  3503.     
  3504.     DmRecordInfo (AddrDB, CurrentRecord, NULL, &uniqueID, NULL);
  3505.     if (BusinessCardRecordID == uniqueID)
  3506.         FrmShowObject(formP, FrmGetObjectIndex (formP, EditViewBusinessCardBmp));
  3507.     else
  3508.         FrmHideObject(formP, FrmGetObjectIndex (formP, EditViewBusinessCardBmp));
  3509.     
  3510. }   
  3511.  
  3512.  
  3513. /***********************************************************************
  3514.  *
  3515.  * FUNCTION:    EditViewResizeDescription
  3516.  *
  3517.  * DESCRIPTION: This routine is called when the height of address
  3518.  *              field is changed as a result of user input.
  3519.  *              If the new height of the field is shorter, more items
  3520.  *              may need to be added to the bottom of the list.
  3521.  *              
  3522.  * PARAMETERS:  event  - a pointer to an EventType structure
  3523.  *
  3524.  * RETURNED:    nothing
  3525.  *
  3526.  * REVISION HISTORY:
  3527.  *         Name   Date      Description
  3528.  *         ----   ----      -----------
  3529.  *         art   6/26/95      Initial Revision
  3530.  *
  3531.  ***********************************************************************/
  3532. static void EditViewResizeDescription (EventPtr event)
  3533. {
  3534.     Word pos;
  3535.     Word row;
  3536.     Word column;
  3537.     Word lastRow;
  3538.     Word fieldIndex;
  3539.     Word lastFieldIndex;
  3540.     Word topFieldIndex;
  3541.     FieldPtr fld;
  3542.     TablePtr table;
  3543.     Boolean restoreFocus = false;
  3544.     Boolean lastItemClipped;
  3545.     RectangleType itemR;
  3546.     RectangleType tableR;
  3547.     RectangleType fieldR;
  3548.  
  3549.  
  3550.     // Get the current height of the field;
  3551.     fld = event->data.fldHeightChanged.pField;
  3552.     FldGetBounds (fld, &fieldR);
  3553.  
  3554.     // Have the table object resize the field and move the items below
  3555.     // the field up or down.
  3556.     table = GetObjectPtr (EditTable);
  3557.     TblHandleEvent (table, event);
  3558.  
  3559.     // If the field's height has expanded , we're done.
  3560.     if (event->data.fldHeightChanged.newHeight >= fieldR.extent.y)
  3561.       {
  3562.       topFieldIndex = TblGetRowID (table, 0);
  3563.       if (topFieldIndex != TopVisibleFieldIndex)
  3564.          TopVisibleFieldIndex = topFieldIndex;
  3565.       else
  3566.          {
  3567.          // Since the table has expanded we may be able to scroll
  3568.          // when before we might not have.
  3569.          lastRow = TblGetLastUsableRow (table);
  3570.          TblGetBounds (table, &tableR);
  3571.          TblGetItemBounds (table, lastRow, editDataColumn, &itemR);
  3572.          lastItemClipped = (itemR.topLeft.y + itemR.extent.y > 
  3573.              tableR.topLeft.y + tableR.extent.y);
  3574.          lastFieldIndex = TblGetRowID (table, lastRow);
  3575.  
  3576.          EditViewUpdateScrollers (FrmGetActiveForm (), lastFieldIndex, 
  3577.             lastItemClipped);
  3578.          
  3579.          return;
  3580.          }
  3581.       }
  3582.  
  3583.     // If the field's height has contracted and the field edit field
  3584.     // is not visible then the table may be scrolled.  Release the 
  3585.     // focus,  which will force the saving of the field we are editing.
  3586.     else if (TblGetRowID (table, 0) != editFirstFieldIndex)
  3587.         {
  3588.         TblGetSelection (table, &row, &column);
  3589.         fieldIndex = TblGetRowID (table, row);
  3590.         
  3591.         fld = TblGetCurrentField (table);
  3592.         pos = FldGetInsPtPosition (fld);
  3593.         TblReleaseFocus (table);
  3594.  
  3595.         restoreFocus = true;
  3596.         }
  3597.  
  3598.     // Add items to the table to fill in the space made available by the 
  3599.     // shorting the field.
  3600.     EditViewLoadTable ();
  3601.     TblRedrawTable (table);
  3602.  
  3603.     // Restore the insertion point position.
  3604.     if (restoreFocus)
  3605.         {
  3606.         TblFindRowID (table, fieldIndex, &row);
  3607.         TblGrabFocus (table, row, column);
  3608.         FldSetInsPtPosition (fld, pos);
  3609.         FldGrabFocus (fld);
  3610.         }
  3611. }
  3612.  
  3613.  
  3614. /***********************************************************************
  3615.  *
  3616.  * FUNCTION:    EditViewScroll
  3617.  *
  3618.  * DESCRIPTION: This routine scrolls the list of editable fields
  3619.  *              in the direction specified.
  3620.  *
  3621.  * PARAMETERS:  direction - up or dowm
  3622.  *              oneLine   - if true the list is scroll by a single line,
  3623.  *                          if false the list is scroll by a full screen.
  3624.  *
  3625.  * RETURNED:    nothing
  3626.  *
  3627.  * REVISION HISTORY:
  3628.  *         Name   Date      Description
  3629.  *         ----   ----      -----------
  3630.  *         art   6/26/95   Initial Revision
  3631.  *         vmk   2/20/98   Move TblUnhighlightSelection before EditViewLoadTable
  3632.  *
  3633.  ***********************************************************************/
  3634. static void EditViewScroll (DirectionType direction)
  3635. {
  3636.    Word            row;
  3637.    Word            height;
  3638.    Word             fieldIndex;
  3639.    Word             columnWidth;
  3640.    Word             tableHeight;
  3641.    TablePtr       table;
  3642.    FontID         curFont;
  3643.    RectangleType   r;
  3644.    AddrDBRecordType record;
  3645.    Handle recordH;
  3646.  
  3647.  
  3648.    curFont = FntSetFont (stdFont);
  3649.    
  3650.    table = GetObjectPtr (EditTable);
  3651.    TblReleaseFocus (table);
  3652.  
  3653.    // Get the height of the table and the width of the description
  3654.    // column.
  3655.    TblGetBounds (table, &r);
  3656.    tableHeight = r.extent.y;
  3657.    height = 0;
  3658.    columnWidth = TblGetColumnWidth (table, editDataColumn);
  3659.  
  3660.    // Scroll the table down.
  3661.    if (direction == down)
  3662.       {
  3663.       // Get the index of the last visible field, this will become 
  3664.       // the index of the top visible field, unless it occupies the 
  3665.       // whole screeen, in which case the next field will be the
  3666.       // top filed.
  3667.       
  3668.       row = TblGetLastUsableRow (table);
  3669.       fieldIndex = TblGetRowID (table, row);
  3670.       
  3671.       // If the last visible field is also the first visible field
  3672.       // then it occupies the whole screeen.
  3673.       if (row == 0)
  3674.          fieldIndex = min (editLastFieldIndex, fieldIndex+1);
  3675.       }
  3676.  
  3677.    // Scroll the table up.
  3678.    else
  3679.       {
  3680.       // Scan the fields before the first visible field to determine 
  3681.       // how many fields we need to scroll.  Since the heights of the 
  3682.       // fields vary,  we sum the height of the records until we get
  3683.       // a screen full.
  3684.  
  3685.       fieldIndex = TblGetRowID (table, 0);
  3686.       ErrFatalDisplayIf(fieldIndex > editLastFieldIndex, "Invalid field Index");
  3687.       if (fieldIndex == 0)
  3688.          goto exit;
  3689.          
  3690.       // Get the current record
  3691.       AddrGetRecord (AddrDB, CurrentRecord, &record, &recordH);
  3692.  
  3693.       height = TblGetRowHeight (table, 0);
  3694.       if (height >= tableHeight)
  3695.          height = 0;                     
  3696.  
  3697.       while (height < tableHeight && fieldIndex > 0)
  3698.          {
  3699.          height += FldCalcFieldHeight (record.fields[FieldMap[fieldIndex-1]], 
  3700.             columnWidth) * FntLineHeight ();
  3701.          if ((height <= tableHeight) || (fieldIndex == TblGetRowID (table, 0)))
  3702.             fieldIndex--;
  3703.          }
  3704.       MemHandleUnlock(recordH);
  3705.       }
  3706.  
  3707.    TblMarkTableInvalid (table);
  3708.     CurrentFieldIndex = noFieldIndex;
  3709.    TopVisibleFieldIndex = fieldIndex;
  3710.    EditRowIDWhichHadFocus = editFirstFieldIndex;
  3711.    EditFieldPosition = 0;
  3712.  
  3713.    TblUnhighlightSelection (table);        // remove the highlight before reloading the table to avoid
  3714.                                                    // having an out of bounds selection information in case
  3715.                                                    // the newly loaded data doesn't have as many rows as the old
  3716.                                                    // data.  This fixes the bug
  3717.                                                    // "File: Table.c, Line: 2599, currentRow violated constraint!"
  3718.                                                    // (fix suggested by Art) vmk 2/20/98
  3719.    EditViewLoadTable ();   
  3720.  
  3721.    //TblUnhighlightSelection (table);    // moved call before EditViewLoadTable vmk 2/20/98
  3722.    TblRedrawTable (table);
  3723.  
  3724. exit:   
  3725.    FntSetFont (curFont);
  3726. }
  3727.  
  3728.  
  3729. /***********************************************************************
  3730.  *
  3731.  * FUNCTION:    EditViewHandleSelectField
  3732.  *
  3733.  * DESCRIPTION: Handle the user tapping an edit view field label.
  3734.  *   Either the a phone label is changed or the user wants to edit
  3735.  * a field by tapping on it's label.
  3736.  *
  3737.  * PARAMETERS:  event  - a pointer to an EventType structure
  3738.  *
  3739.  * RETURNED:    true if the event was handled and nothing else should
  3740.  *                be done
  3741.  *
  3742.  * REVISION HISTORY:
  3743.  *       Name   Date        Description
  3744.  *       ----    ----        -----------
  3745.  *        roger    11/27/95    Cut from EditViewHandleEvent
  3746.  *        art        9/2/97        Add multi-font support
  3747.  *        roger    11/4/97        Changed parameters to support another routine
  3748.  *
  3749.  ***********************************************************************/
  3750. static Boolean EditViewHandleSelectField (Word row, const Byte column)
  3751. {
  3752.     Err                    err;
  3753.     Word                currRow;
  3754.     Word                fieldNum;
  3755.     Word                fieldIndex;
  3756.     Handle                currentRecordH;
  3757.     UInt                i;
  3758.     UInt                currentField;
  3759.     FontID                currFont;
  3760.     Boolean                redraw = false;
  3761.     TablePtr            tableP;
  3762.     FieldPtr            fldP;
  3763.     AddrDBRecordType    currentRecord;
  3764.     AddrDBRecordFlags    changedFields;
  3765.     
  3766.     
  3767.     tableP = GetObjectPtr(EditTable);
  3768.     
  3769.     // If a phone label was changed then modify the record
  3770.     currentField = FieldMap[TblGetRowID(tableP, row)];
  3771.     if (column == editLabelColumn)
  3772.         {
  3773.         if (isPhoneField(currentField))
  3774.             {
  3775.             i = TblGetItemInt(tableP, row, editLabelColumn);
  3776.             AddrGetRecord(AddrDB, CurrentRecord, ¤tRecord, ¤tRecordH);
  3777.             
  3778.             switch (currentField)
  3779.                 {
  3780.                 case firstPhoneField:
  3781.                     currentRecord.options.phones.phone1 = i;
  3782.                     break;
  3783.  
  3784.                 case firstPhoneField + 1:
  3785.                     currentRecord.options.phones.phone2 = i;
  3786.                     break;
  3787.  
  3788.                 case firstPhoneField + 2:
  3789.                     currentRecord.options.phones.phone3 = i;
  3790.                     break;
  3791.  
  3792.                 case firstPhoneField + 3:
  3793.                     currentRecord.options.phones.phone4 = i;
  3794.                     break;
  3795.  
  3796.                 case firstPhoneField + 4:
  3797.                     currentRecord.options.phones.phone5 = i;
  3798.                     break;
  3799.                 }
  3800.  
  3801.             changedFields.allBits = 0;
  3802.             err = AddrChangeRecord(AddrDB, &CurrentRecord, ¤tRecord,
  3803.                 changedFields);
  3804.             if (err)
  3805.                 {
  3806.                 MemHandleUnlock(currentRecordH);
  3807.                 FrmAlert(DeviceFullAlert);
  3808.  
  3809.                 // Redraw the table without the change.  The phone label
  3810.                 // is unchanged in the record but the screen and the table row
  3811.                 // are changed.  Reinit the table to fix it.  Mark the row
  3812.                 // invalid and redraw it.
  3813.                 EditViewInit (FrmGetActiveForm(), false);
  3814.                 TblMarkRowInvalid(tableP, row);
  3815.                 TblRedrawTable(tableP);
  3816.  
  3817.                 return true;
  3818.                 }
  3819.             }
  3820.  
  3821.         // The user selected the label of a field.
  3822.         // The user probably wants to edit it so
  3823.         // set the table to edit the field to the right of the label.
  3824.         
  3825.         TblReleaseFocus(tableP);
  3826.         TblUnhighlightSelection(tableP);
  3827. //        setFocus = true;
  3828.         }
  3829.         
  3830.  
  3831.     // Make sure the the heights the the field we are exiting and the 
  3832.     // that we are entering are correct.  They may be incorrect if the 
  3833.     // font used to display blank line is a different height then the
  3834.     // font used to display field text.
  3835.     fieldIndex = TblGetRowID (tableP, row);
  3836.  
  3837.     if (fieldIndex != CurrentFieldIndex || TblGetCurrentField(tableP) == NULL)
  3838.         {
  3839.         AddrGetRecord (AddrDB, CurrentRecord, ¤tRecord, ¤tRecordH);
  3840.  
  3841.         currFont = FntGetFont ();
  3842.  
  3843.         // Is the current field empty?
  3844.         fieldNum = FieldMap[CurrentFieldIndex];
  3845.         if (! currentRecord.fields[fieldNum])
  3846.             {
  3847.             if (TblFindRowID (tableP, CurrentFieldIndex, &currRow))
  3848.                 {
  3849.                 // Is the height of the field correct?
  3850.                 FntSetFont (addrEditBlankFont);
  3851.                 if (FntLineHeight () != TblGetRowHeight (tableP, currRow))
  3852.                     {
  3853.                     TblMarkRowInvalid (tableP, currRow);
  3854.                     redraw = true;
  3855.                     }
  3856.                 }
  3857.             }
  3858.  
  3859.         CurrentFieldIndex = fieldIndex;
  3860.  
  3861.         // Is the newly selected field empty?
  3862.         fieldNum = FieldMap[fieldIndex];
  3863.         if (! currentRecord.fields[fieldNum])
  3864.             {
  3865.             // Is the height of the field correct?
  3866.             FntSetFont (AddrEditFont);
  3867.             if (FntLineHeight () != TblGetRowHeight (tableP, row))
  3868.                 {
  3869.                 TblMarkRowInvalid (tableP, row);
  3870.                 redraw = true;
  3871.                 }
  3872.             }
  3873.         
  3874.         // Do before the table focus is released and the record is saved.
  3875.         MemHandleUnlock (currentRecordH);
  3876.         
  3877.         if (redraw)
  3878.             {
  3879.             TblReleaseFocus (tableP);
  3880.             EditViewLoadTable ();
  3881.             TblFindRowID (tableP, fieldIndex, &row);
  3882.  
  3883.             TblRedrawTable (tableP);
  3884. //            setFocus = true;
  3885.             }
  3886.  
  3887.         FntSetFont (currFont);
  3888.         }
  3889.  
  3890.  
  3891. //    if (TblGetCurrentField(tableP) != NULL)
  3892. //        TblReleaseFocus (tableP);
  3893.     
  3894.     // Set the focus
  3895.     if (TblGetCurrentField(tableP) == NULL)
  3896.         {
  3897.         TblGrabFocus (tableP, row, editDataColumn);
  3898.         fldP = TblGetCurrentField(tableP);
  3899.         FldGrabFocus (fldP);
  3900.         FldMakeFullyVisible (fldP);
  3901.         }
  3902.  
  3903.     
  3904.     return false;
  3905.     
  3906. }
  3907.  
  3908.  
  3909. /***********************************************************************
  3910.  *
  3911.  * FUNCTION:    EditViewNextField
  3912.  *
  3913.  * DESCRIPTION: If a field is being edited, advance the focus to the
  3914.  * edit view table's next field.
  3915.  *
  3916.  * PARAMETERS:  nothing
  3917.  *
  3918.  * RETURNED:    nothing
  3919.  *
  3920.  * REVISION HISTORY:
  3921.  *         Name   Date      Description
  3922.  *         ----   ----      -----------
  3923.  *         roger   7/27/95   Initial Revision
  3924.  *
  3925.  ***********************************************************************/
  3926. static void EditViewNextField (DirectionType direction)
  3927. {
  3928.    TablePtr tableP;
  3929.    Word row;
  3930.    Word column;
  3931.    UInt nextFieldNumIndex;
  3932.    
  3933.    
  3934.    tableP = GetObjectPtr (EditTable);
  3935.    
  3936.    if (!TblEditing(tableP))
  3937.       return;
  3938.    
  3939.    // Find out which field is being edited.
  3940.    TblGetSelection (tableP, &row, &column);
  3941.    nextFieldNumIndex = TblGetRowID (tableP, row);
  3942.    if (direction == down)
  3943.       {
  3944.       if (nextFieldNumIndex >= editLastFieldIndex)
  3945.          nextFieldNumIndex = 0;
  3946.       else
  3947.          nextFieldNumIndex++;
  3948.       }
  3949.    else
  3950.       {
  3951.       if (nextFieldNumIndex == 0)
  3952.          nextFieldNumIndex = editLastFieldIndex;
  3953.       else
  3954.          nextFieldNumIndex--;
  3955.       }
  3956.    TblReleaseFocus (tableP);
  3957.  
  3958.     CurrentFieldIndex = nextFieldNumIndex;
  3959.  
  3960.    // If the new field isn't visible move the edit view and then
  3961.    // find the row where the next field is.
  3962.    while (!TblFindRowID(tableP, nextFieldNumIndex, &row))
  3963.       {
  3964.       // Scroll the view down placing the item
  3965.       // on the top row
  3966.       TopVisibleFieldIndex = nextFieldNumIndex;
  3967.       EditViewLoadTable();
  3968.       TblRedrawTable(tableP);
  3969.       }
  3970.  
  3971.    
  3972.    // Select the item
  3973. #if 0
  3974.    TblGrabFocus (tableP, row, editDataColumn);
  3975.    fldP = TblGetCurrentField (tableP);
  3976.    FldSetInsPtPosition (fldP, 0);
  3977.    FldGrabFocus (fldP);
  3978.    FldMakeFullyVisible (fldP);
  3979. #endif
  3980.  
  3981.    EditViewHandleSelectField(row, editDataColumn);
  3982. }
  3983.  
  3984.  
  3985.  
  3986.  
  3987. /***********************************************************************
  3988.  *
  3989.  * FUNCTION:    EditViewUpdateCustomFieldLabels
  3990.  *
  3991.  * DESCRIPTION: Update the custom field labels by reloading those rows
  3992.  *
  3993.  * PARAMETERS:  event  - a pointer to an EventType structure
  3994.  *
  3995.  * RETURNED:    true if the event has handle and should not be passed
  3996.  *              to a higher level handler.
  3997.  *
  3998.  * REVISION HISTORY:
  3999.  *         Name   Date      Description
  4000.  *         ----   ----      -----------
  4001.  *         art   6/5/95      Initial Revision
  4002.  *
  4003.  ***********************************************************************/
  4004. static void EditViewUpdateCustomFieldLabels ()
  4005. {
  4006.    FormPtr frm;
  4007.    Word row;
  4008.    Word rowsInTable;
  4009.    TablePtr table;
  4010.    AddrAppInfoPtr appInfoPtr;
  4011.    UInt fieldIndex;
  4012.    UInt fieldNum;
  4013.    AddrDBRecordType record;
  4014.    Handle recordH;
  4015.    Boolean redraw = false;
  4016.  
  4017.  
  4018.    appInfoPtr = (AddrAppInfoPtr) AddrAppInfoGetPtr(AddrDB);
  4019.    frm = FrmGetActiveForm ();
  4020.    table = GetObjectPtr (EditTable);
  4021.  
  4022.    if (TblGetColumnWidth(table, editLabelColumn) != EditLabelColumnWidth)
  4023.       {
  4024.       EditViewInit (frm, false);
  4025.       redraw = true;
  4026.       }
  4027.    else
  4028.       {
  4029.       
  4030.    
  4031.       // Get the current record
  4032.       AddrGetRecord (AddrDB, CurrentRecord, &record, &recordH);
  4033.    
  4034.       
  4035.       rowsInTable = TblGetNumberOfRows(table);
  4036.    
  4037.       // Reload any renameable fields
  4038.       for (row = 0; row < rowsInTable; row++)
  4039.          {
  4040.          if (TblRowUsable (table, row))
  4041.             {
  4042.             fieldIndex = TblGetRowID (table, row);
  4043.             fieldNum = FieldMap[fieldIndex];
  4044.             if (fieldNum >= firstRenameableLabel &&
  4045.                fieldNum <= lastRenameableLabel)
  4046.                {
  4047.                     EditInitTableRow (table, row, fieldIndex, 
  4048.                         TblGetRowHeight (table, row),
  4049.                         TblGetItemFont (table, row, editDataColumn),
  4050.                         &record, appInfoPtr);
  4051.                redraw = true;
  4052.    
  4053.                // Mark the row invalid so that it will draw when we call the 
  4054.                // draw routine.
  4055.                TblMarkRowInvalid (table, row);
  4056.                }
  4057.             }
  4058.          }
  4059.    
  4060.    
  4061.       MemHandleUnlock(recordH);
  4062.       }
  4063.       
  4064.       
  4065.    if (redraw)
  4066.       TblRedrawTable(table);
  4067.  
  4068.       
  4069.    MemPtrUnlock(appInfoPtr);
  4070. }
  4071.  
  4072.  
  4073. /***********************************************************************
  4074.  *
  4075.  * FUNCTION:    EditViewUpdateDisplay
  4076.  *
  4077.  * DESCRIPTION: This routine update the display of the edit view
  4078.  *
  4079.  * PARAMETERS:  updateCode - a code that indicated what changes  been
  4080.  *                           have made to the view.
  4081.  *                        
  4082.  * RETURNED:    nothing
  4083.  *
  4084.  * REVISION HISTORY:
  4085.  *            Name    Date        Description
  4086.  *            ----    ----        -----------
  4087.  *            art    9/12/97    Initial Revision
  4088.  *
  4089.  ***********************************************************************/
  4090. static void EditViewUpdateDisplay (Word updateCode)
  4091. {
  4092.     TablePtr table;
  4093.  
  4094.  
  4095.     if (updateCode & updateCustomFieldLabelChanged)
  4096.         {
  4097.         EditViewUpdateCustomFieldLabels();
  4098.         }
  4099.  
  4100.     if (updateCode & updateCategoryChanged)
  4101.         {
  4102.         // Set the label of the category trigger.
  4103.         CategoryGetName (AddrDB, CurrentCategory, CategoryName);
  4104.         CategorySetTriggerLabel (GetObjectPtr (EditCategoryTrigger), CategoryName);
  4105.         }
  4106.  
  4107.     if (updateCode & updateGrabFocus)
  4108.         {
  4109.         EditViewRestoreEditState ();
  4110.         }
  4111.  
  4112.     if (updateCode & updateFontChanged)
  4113.         {
  4114.         table = GetObjectPtr (EditTable);
  4115.         TblReleaseFocus (table);
  4116.         EditViewLoadTable ();
  4117.         TblRedrawTable (table);
  4118.         EditViewRestoreEditState ();
  4119.         }
  4120. }
  4121.  
  4122.  
  4123. /***********************************************************************
  4124.  *
  4125.  * FUNCTION:    EditViewDoCommand
  4126.  *
  4127.  * DESCRIPTION: This routine performs the menu command specified.
  4128.  *
  4129.  * PARAMETERS:  command  - menu item id
  4130.  *
  4131.  * RETURNED:    nothing
  4132.  *
  4133.  * REVISION HISTORY:
  4134.  *         Name   Date      Description
  4135.  *         ----   ----      -----------
  4136.  *         roger   6/27/95   Initial Revision
  4137.  *
  4138.  ***********************************************************************/
  4139. static Boolean EditViewDoCommand (Word command)
  4140. {
  4141.     TablePtr tableP;
  4142.     AddrDBRecordType record;
  4143.     Handle recordH;
  4144.     Boolean hasNote;
  4145.  
  4146.  
  4147.     switch (command)
  4148.         {
  4149.         case EditRecordDeleteRecordCmd:
  4150.             MenuEraseStatus (0);
  4151.             TblReleaseFocus(GetObjectPtr(EditTable));   // save the field
  4152.             if (DetailsDeleteRecord ())
  4153.                 FrmGotoForm (ListView);
  4154.             else
  4155.                 EditViewRestoreEditState ();
  4156.             return true;
  4157.             break;
  4158.  
  4159.         case EditRecordAttachNoteCmd:
  4160.             MenuEraseStatus (0);
  4161.             TblReleaseFocus(GetObjectPtr(EditTable));   // save the field
  4162.             if (CreateNote())
  4163.                 FrmGotoForm (NoteView);
  4164.             return true;
  4165.             break;
  4166.  
  4167.         case EditRecordDeleteNoteCmd:
  4168.             MenuEraseStatus (0);
  4169.             AddrGetRecord (AddrDB, CurrentRecord, &record, &recordH);
  4170.             hasNote = (record.fields[note] != NULL);
  4171.             MemHandleUnlock(recordH);
  4172.             if (hasNote && 
  4173.                 FrmAlert(DeleteNoteAlert) == DeleteNoteYes)
  4174.             DeleteNote ();
  4175.             return true;
  4176.             break;
  4177.  
  4178.         case EditRecordSelectBusinessCardCmd:
  4179.             MenuEraseStatus (0);
  4180.             if (FrmAlert(SelectBusinessCardAlert) == SelectBusinessCardYes)
  4181.                 {
  4182.                 DmRecordInfo (AddrDB, CurrentRecord, NULL, &BusinessCardRecordID, NULL);
  4183.                 EditViewDrawBusinessCardIndicator (FrmGetActiveForm());
  4184.                 }
  4185.             return true;
  4186.             break;
  4187.  
  4188.         case EditRecordSendBusinessCardCmd:
  4189.             MenuEraseStatus (0);
  4190.             AddrSendBusinessCard(AddrDB);
  4191.             return true;
  4192.             break;
  4193.  
  4194.         case EditRecordSendRecordCmd:
  4195.             // Make sure the field being edited is saved
  4196.             tableP = GetObjectPtr (EditTable);
  4197.             TblReleaseFocus(tableP);
  4198.  
  4199.             MenuEraseStatus (0);
  4200.             AddrSendRecord(AddrDB, CurrentRecord);
  4201.             return true;
  4202.             break;
  4203.  
  4204.         case EditOptionsFontCmd:
  4205.             MenuEraseStatus (0);
  4206.             AddrEditFont = SelectFont (AddrEditFont);
  4207.             return true;
  4208.             break;
  4209.  
  4210.         case EditOptionsEditCustomFldsCmd:
  4211.             MenuEraseStatus (0);
  4212.             TblReleaseFocus(GetObjectPtr( EditTable));   // save the field
  4213.             FrmPopupForm (CustomEditDialog);
  4214.             return true;
  4215.             break;
  4216.  
  4217.         case EditOptionsAboutCmd:
  4218.             MenuEraseStatus (0);
  4219.             AbtShowAbout (sysFileCAddress);
  4220.             return true;
  4221.             break;
  4222.  
  4223.         default:
  4224.             break;
  4225.         }
  4226.  
  4227.     return false;
  4228. }
  4229.  
  4230.  
  4231. /***********************************************************************
  4232.  *
  4233.  * FUNCTION:    EditLabelColumnWidthInit
  4234.  *
  4235.  * DESCRIPTION: Set EditLabelColumnWidth to the width of the widest
  4236.  * field label plus a ':'.  This routine should be called once per run.
  4237.  *
  4238.  * PARAMETERS:  appInfoPtr - pointer to the app info block for field labels
  4239.  *
  4240.  * RETURNED:    EditLabelColumnWidth is set
  4241.  *
  4242.  * REVISION HISTORY:
  4243.  *         Name   Date      Description
  4244.  *         ----   ----      -----------
  4245.  *         roger   8/3/95   Initial Revision
  4246.  *
  4247.  ***********************************************************************/
  4248. /* remove 1/30/98 Art
  4249.  
  4250. static void EditLabelColumnWidthInit (AddrAppInfoPtr appInfoPtr)
  4251. {
  4252.    Int maxWidth, i;
  4253.    FontID curFont;
  4254.    UInt labelWidth;      // Width of a field label
  4255.    UInt columnWidth;      // Width of the label column (fits all label)
  4256.    UInt tableWidth;      // Width of the label and data columns 
  4257.    
  4258.    
  4259.    ErrNonFatalDisplayIf(EditLabelColumnWidth != 0, "EditLabelColumnWidth already set");
  4260.    
  4261.    // no wider than the window
  4262.    WinGetWindowExtent ((short *) &maxWidth, (short *) &i);
  4263.  
  4264.    
  4265.    // Calculate EditLabelColumnWidth which is used by the Record View as
  4266.    // the amount of indentation.
  4267.    curFont = FntSetFont (stdFont);
  4268.    if (EditLabelColumnWidth == 0)
  4269.       {
  4270.       tableWidth = maxWidth - spaceBeforeDesc;      // less the table column gutter
  4271.       columnWidth = 0;
  4272.           
  4273.       for (i = firstAddressField; i < lastLabel; i ++)
  4274.          {
  4275.          labelWidth = FntCharsWidth(appInfoPtr->fieldLabels[i], 
  4276.             StrLen(appInfoPtr->fieldLabels[i]));
  4277.          columnWidth = max(columnWidth, labelWidth);
  4278.          }
  4279.       columnWidth += 1 + FntCharWidth(':');
  4280.       
  4281.       EditLabelColumnWidth = columnWidth;
  4282.       EditDataColumnWidth = tableWidth - columnWidth;
  4283.       }
  4284.    
  4285.    FntSetFont (curFont);
  4286. }
  4287. */
  4288.  
  4289.  
  4290. /***********************************************************************
  4291.  *
  4292.  * FUNCTION:    EditViewInit
  4293.  *
  4294.  * DESCRIPTION: This routine initializes the "Edit View" of the 
  4295.  *              Address application.
  4296.  *
  4297.  * PARAMETERS:  event  - a pointer to an EventType structure
  4298.  *
  4299.  * RETURNED:    true if the event has handle and should not be passed
  4300.  *              to a higher level handler.
  4301.  *
  4302.  * REVISION HISTORY:
  4303.  *         Name   Date      Description
  4304.  *         ----   ----      -----------
  4305.  *         art   6/5/95      Initial Revision
  4306.  *
  4307.  ***********************************************************************/
  4308. static void EditViewInit (FormPtr frm, Boolean leaveAppInfoUnlocked)
  4309.     {
  4310.     UInt attr;
  4311.     Word row;
  4312.     Word rowsInTable;
  4313.     Word category;
  4314.     Word dataColumnWidth;
  4315.     TablePtr table;
  4316.     AddrAppInfoPtr appInfoPtr;
  4317.     ListPtr popupPhoneList;
  4318.     FontID   currFont;
  4319.     RectangleType bounds;
  4320.  
  4321.  
  4322.     currFont = FntSetFont (stdFont);
  4323.     appInfoPtr = (AddrAppInfoPtr) AddrAppInfoGetPtr(AddrDB);
  4324.  
  4325.     // Set the choices to the phone list
  4326.     EditPhoneListChoices[0] = appInfoPtr->fieldLabels[firstPhoneField];
  4327.     EditPhoneListChoices[1] = appInfoPtr->fieldLabels[firstPhoneField + 1];
  4328.     EditPhoneListChoices[2] = appInfoPtr->fieldLabels[firstPhoneField + 2];
  4329.     EditPhoneListChoices[3] = appInfoPtr->fieldLabels[firstPhoneField + 3];
  4330.     EditPhoneListChoices[4] = appInfoPtr->fieldLabels[firstPhoneField + 4];
  4331.     EditPhoneListChoices[5] = appInfoPtr->fieldLabels[addressFieldsCount];
  4332.     EditPhoneListChoices[6] = appInfoPtr->fieldLabels[addressFieldsCount + 1];
  4333.     EditPhoneListChoices[7] = appInfoPtr->fieldLabels[addressFieldsCount + 2];
  4334.     popupPhoneList = GetObjectPtr (EditPhoneList);
  4335.     LstSetListChoices(popupPhoneList, EditPhoneListChoices, numPhoneLabels);
  4336.     LstSetHeight (popupPhoneList, numPhoneLabels);
  4337.  
  4338.  
  4339.  
  4340.     // Initialize the address list table.
  4341.     table = FrmGetObjectPtr (frm, FrmGetObjectIndex (frm, EditTable));
  4342.     rowsInTable = TblGetNumberOfRows (table);
  4343.     for (row = 0; row < rowsInTable; row++)
  4344.       {
  4345.       // This sets the data column
  4346.       TblSetItemStyle (table, row, editDataColumn, textTableItem);
  4347.       TblSetRowUsable (table, row, false);
  4348.       }
  4349.  
  4350.     TblSetColumnUsable (table, editLabelColumn, true);
  4351.     TblSetColumnUsable (table, editDataColumn, true);
  4352.  
  4353.     TblSetColumnSpacing (table, editLabelColumn, spaceBeforeDesc);
  4354.  
  4355.  
  4356.     // Set the callback routines that will load and save the 
  4357.     // description field.
  4358.     TblSetLoadDataProcedure (table, editDataColumn, EditViewGetRecordField);
  4359.     TblSetSaveDataProcedure (table, editDataColumn, EditViewSaveRecordField);
  4360.  
  4361.  
  4362.     // Set the column widths so that the label column contents fit exactly.
  4363.     // Those labels change as the country changes.
  4364.     if (EditLabelColumnWidth == 0)
  4365.         EditLabelColumnWidth = GetLabelColumnWidth (appInfoPtr, stdFont);
  4366.         
  4367.     // Compute the width of the data column, account for the table column gutter.
  4368.     TblGetBounds (table, &bounds);
  4369.     dataColumnWidth = bounds.extent.x - spaceBeforeDesc - EditLabelColumnWidth;
  4370.  
  4371.     TblSetColumnWidth(table, editLabelColumn, EditLabelColumnWidth);
  4372.     TblSetColumnWidth(table, editDataColumn, dataColumnWidth);
  4373.       
  4374.  
  4375.     EditViewLoadTable ();
  4376.  
  4377.  
  4378.     // Set the label of the category trigger.
  4379.     if (CurrentCategory == dmAllCategories)
  4380.       {
  4381.       DmRecordInfo (AddrDB, CurrentRecord, &attr, NULL, NULL);   
  4382.       category = attr & dmRecAttrCategoryMask;
  4383.       }
  4384.     else 
  4385.       category = CurrentCategory;
  4386.     CategoryGetName (AddrDB, category, CategoryName);
  4387.     CategorySetTriggerLabel ( GetObjectPtr (EditCategoryTrigger), CategoryName);
  4388.  
  4389.  
  4390.     FntSetFont (currFont);
  4391.  
  4392.     // true when called during frmOpenEvent.
  4393.     // appInfoPtr is unlocked by the frmCloseEvent
  4394.     if (!leaveAppInfoUnlocked)
  4395.       MemPtrUnlock(appInfoPtr);
  4396.  
  4397.     // In general, the record isn't needed after this form is closed.
  4398.     // It is if the user is going to the Note View.  In that case
  4399.     // we must keep the record.
  4400.     RecordNeededAfterEditView = false;
  4401. }
  4402.  
  4403.  
  4404. /***********************************************************************
  4405.  *
  4406.  * FUNCTION:    EditViewHandleEvent
  4407.  *
  4408.  * DESCRIPTION: This routine is the event handler for the "Edit View"
  4409.  *              of the Address Book application.
  4410.  *
  4411.  * PARAMETERS:  event  - a pointer to an EventType structure
  4412.  *
  4413.  * RETURNED:    true if the event has handle and should not be passed
  4414.  *              to a higher level handler.
  4415.  *
  4416.  * REVISION HISTORY:
  4417.  *         Name   Date      Description
  4418.  *         ----   ----      -----------
  4419.  *         art   6/5/95   Initial Revision
  4420.  *
  4421.  ***********************************************************************/
  4422. static Boolean EditViewHandleEvent (EventPtr event)
  4423. {
  4424.    FormPtr frm;
  4425.    TablePtr tableP;
  4426.    Boolean handled = false;
  4427.    Word row;
  4428.  
  4429.  
  4430.    switch (event->eType)
  4431.        {
  4432.       case keyDownEvent:
  4433.          if (TxtCharIsHardKey(event->data.keyDown.modifiers, event->data.keyDown.chr))
  4434.              {
  4435.             TblReleaseFocus(GetObjectPtr(EditTable));
  4436.             TopVisibleRecord = 0;      // Same as when app switched to
  4437.                 CurrentFieldIndex = noFieldIndex;
  4438.             FrmGotoForm (ListView);
  4439.             return (true);
  4440.              }
  4441.  
  4442.          switch (event->data.keyDown.chr)
  4443.              {
  4444.             case pageUpChr:
  4445.                EditViewScroll (up);
  4446.                handled = true;
  4447.                break;
  4448.                
  4449.             case pageDownChr:
  4450.                EditViewScroll (down);
  4451.                handled = true;
  4452.                break;
  4453.  
  4454.             case nextFieldChr:
  4455.                EditViewNextField (down);
  4456.                handled = true;
  4457.                break;
  4458.  
  4459.             case prevFieldChr:
  4460.                EditViewNextField (up);
  4461.                handled = true;
  4462.                break;
  4463.             
  4464.             case sendDataChr:
  4465.                // Make sure the field being edited is saved
  4466.                frm = FrmGetActiveForm ();
  4467.                tableP = FrmGetObjectPtr (frm, FrmGetObjectIndex(frm, EditTable));
  4468.                TblReleaseFocus(tableP);
  4469.                
  4470.                 MenuEraseStatus (0);
  4471.                 AddrSendRecord(AddrDB, CurrentRecord);
  4472.                handled = true;
  4473.                break;
  4474.             
  4475.             default:
  4476.                break;
  4477.              }
  4478.          break;
  4479.  
  4480.  
  4481.       case ctlSelectEvent:
  4482.          switch (event->data.ctlSelect.controlID){
  4483.             case EditCategoryTrigger:
  4484.                EditViewSelectCategory ();
  4485.                      EditViewRestoreEditState ();
  4486.               handled = true;
  4487.                break;
  4488.  
  4489.             case EditDoneButton:
  4490.                FrmGotoForm (ListView);
  4491.                handled = true;
  4492.                break;
  4493.  
  4494.             case EditDetailsButton:
  4495.                FrmPopupForm (DetailsDialog);
  4496.                handled = true;
  4497.                break;
  4498.  
  4499.             case EditNoteButton:
  4500.                if (CreateNote())
  4501.                   {
  4502.                   RecordNeededAfterEditView = true;
  4503.                   FrmGotoForm (NoteView);
  4504.                   }
  4505.                handled = true;
  4506.                break;
  4507.             default:
  4508.                break;
  4509.  
  4510.          }
  4511.          break;
  4512.  
  4513.       case ctlRepeatEvent:
  4514.          switch (event->data.ctlRepeat.controlID){
  4515.             case EditUpButton:
  4516.                EditViewScroll (up);
  4517.                // leave unhandled so the buttons can repeat
  4518.                break;
  4519.                
  4520.             case EditDownButton:
  4521.                EditViewScroll (down);
  4522.                // leave unhandled so the buttons can repeat
  4523.                break;
  4524.             default:
  4525.                break;
  4526.          }
  4527.          break;
  4528.  
  4529.       case tblSelectEvent:
  4530.           // Select the field if it's different than the one selected before.  This means the selection
  4531.           // is on a different row or the selection is a phone label.
  4532.           if (CurrentFieldIndex != TblGetRowID (event->data.tblSelect.pTable, event->data.tblSelect.row) ||
  4533.               (event->data.tblSelect.column == editLabelColumn && isPhoneField(FieldMap[
  4534.                   TblGetRowID(event->data.tblSelect.pTable, event->data.tblSelect.row)])))
  4535.              EditViewHandleSelectField (event->data.tblSelect.row, event->data.tblSelect.column);
  4536.          break;
  4537.  
  4538.       case menuEvent:
  4539.          return EditViewDoCommand (event->data.menu.itemID);
  4540.  
  4541.       
  4542.       case fldHeightChangedEvent:
  4543.          EditViewResizeDescription (event);
  4544.          handled = true;
  4545.          break;
  4546.       
  4547.       
  4548.       case frmUpdateEvent:
  4549.          EditViewUpdateDisplay (event->data.frmUpdate.updateCode);
  4550.          handled = true;
  4551.          break;
  4552.  
  4553.       case frmCloseEvent:
  4554.          {
  4555.             AddrAppInfoPtr appInfoPtr;
  4556.             
  4557.             // Check if the record is empty and should be deleted.  This cannot
  4558.             // be done earlier because if the record is deleted there is nothing
  4559.             // to display in the table.
  4560.             EditViewSaveRecord ();
  4561.  
  4562.             // We need to unlock the block containing the phone labels.   
  4563.             appInfoPtr = (AddrAppInfoPtr) AddrAppInfoGetPtr(AddrDB);
  4564.             MemPtrUnlock(appInfoPtr);
  4565.             MemPtrUnlock(appInfoPtr);   // Unlock lock in EditViewInit
  4566.          }
  4567.          break;
  4568.  
  4569.          
  4570.       case frmSaveEvent:
  4571.          // Save the field being edited.  Do not delete the record if it's
  4572.          // empty because a frmSaveEvent can be sent without the form being
  4573.          // closed.  A canceled find does this.
  4574.          tableP = GetObjectPtr (EditTable);
  4575.          TblReleaseFocus(tableP);
  4576.          break;
  4577.  
  4578.          
  4579.       case frmOpenEvent:
  4580.          {
  4581.             UInt tableIndex;
  4582.             FieldPtr fldP;
  4583.  
  4584.             
  4585.             frm = FrmGetActiveForm ();
  4586.             EditViewInit (frm, true);
  4587.             tableIndex = FrmGetObjectIndex(frm, EditTable);
  4588.             tableP = FrmGetObjectPtr (frm, tableIndex);
  4589.             
  4590.             // Make sure the field which will get the focus is visible
  4591.             while (!TblFindRowID (tableP, EditRowIDWhichHadFocus, &row))
  4592.                {
  4593.                TopVisibleFieldIndex = EditRowIDWhichHadFocus;
  4594.                     CurrentFieldIndex = EditRowIDWhichHadFocus;
  4595.                EditViewLoadTable();
  4596.                }
  4597.             FrmDrawForm (frm);
  4598.             EditViewDrawBusinessCardIndicator (frm);
  4599.                      
  4600.             // Now set the focus.
  4601.             FrmSetFocus(frm, tableIndex);
  4602.             TblGrabFocus (tableP, row, editDataColumn);
  4603.             fldP = TblGetCurrentField(tableP);
  4604.             FldGrabFocus (fldP);
  4605.             FldSetInsPtPosition (fldP, EditFieldPosition);
  4606.             
  4607.             PriorAddressFormID = FrmGetFormId (frm);
  4608.             handled = true;
  4609.          }
  4610.          break;
  4611.       default:
  4612.          break;
  4613.    }
  4614.  
  4615.  
  4616.    return (handled);
  4617. }
  4618.  
  4619. /***********************************************************************
  4620.  *
  4621.  * FUNCTION:    EditViewNewRecord
  4622.  *
  4623.  * DESCRIPTION: Makes a new record with some setup
  4624.  *
  4625.  * PARAMETERS:  event  - a pointer to an EventType structure
  4626.  *
  4627.  * RETURNED:    nothing
  4628.  *
  4629.  * REVISION HISTORY:
  4630.  *         Name   Date      Description
  4631.  *         ----   ----      -----------
  4632.  *         roger   6/13/95   Initial Revision
  4633.  *
  4634.  ***********************************************************************/
  4635. static void EditViewNewRecord ()
  4636. {
  4637.    AddrDBRecordType newRecord;
  4638.    AddressFields i;
  4639.    UInt attr;
  4640.    Err err;
  4641.    
  4642.  
  4643.    // Set up the new record
  4644.    newRecord.options.phones.displayPhoneForList = 0;
  4645.    newRecord.options.phones.phone1 = workLabel;
  4646.    newRecord.options.phones.phone2 = homeLabel;
  4647.    newRecord.options.phones.phone3 = faxLabel;
  4648.    newRecord.options.phones.phone4 = otherLabel;
  4649.    newRecord.options.phones.phone5 = emailLabel;
  4650.    
  4651.    for (i = firstAddressField; i < addressFieldsCount; i++)
  4652.       {
  4653.       newRecord.fields[i] = NULL;
  4654.       }
  4655.    
  4656.    err = AddrNewRecord(AddrDB, &newRecord, &CurrentRecord);
  4657.    if (err)
  4658.       {
  4659.       FrmAlert(DeviceFullAlert);
  4660.       return;
  4661.       }
  4662.       
  4663.  
  4664.    // Set it's category to the category being viewed.
  4665.    // If the category is All then set the category to unfiled.
  4666.    DmRecordInfo (AddrDB, CurrentRecord, &attr, NULL, NULL);   
  4667.    attr &= ~dmRecAttrCategoryMask;
  4668.    attr |= ((CurrentCategory == dmAllCategories) ? dmUnfiledCategory : 
  4669.       CurrentCategory) | dmRecAttrDirty;
  4670.    DmSetRecordInfo (AddrDB, CurrentRecord, &attr, NULL);
  4671.  
  4672.  
  4673.    // Set the global variable that determines which field is the top visible
  4674.    // field in the edit view.  Also done when New is pressed.
  4675.    TopVisibleFieldIndex = 0;
  4676.     CurrentFieldIndex = editFirstFieldIndex;
  4677.    EditRowIDWhichHadFocus = editFirstFieldIndex;
  4678.    EditFieldPosition = 0;
  4679.  
  4680.    FrmGotoForm (EditView);
  4681. }
  4682.  
  4683.  
  4684. #pragma mark ----------------
  4685. /***********************************************************************
  4686.  *
  4687.  * FUNCTION:    RecordViewNewLine
  4688.  *
  4689.  * DESCRIPTION: Adds the next field at the start of a new line
  4690.  *
  4691.  * PARAMETERS:  width - width already occupied on the line
  4692.  *
  4693.  * RETURNED:    width is set
  4694.  *
  4695.  * REVISION HISTORY:
  4696.  *         Name   Date      Description
  4697.  *         ----   ----      -----------
  4698.  *         roger   6/21/95   Initial Revision
  4699.  *
  4700.  ***********************************************************************/
  4701. static void RecordViewNewLine (UInt *width)
  4702. {
  4703.    if (RecordViewLastLine >= recordViewLinesMax)
  4704.       return;
  4705.       
  4706.    if (*width == 0)
  4707.       {
  4708.       RecordViewLines[RecordViewLastLine].fieldNum = recordViewBlankLine;
  4709.       RecordViewLines[RecordViewLastLine].x = 0;
  4710.       RecordViewLastLine++;
  4711.       }
  4712.    else
  4713.       *width = 0;
  4714. }
  4715.  
  4716.  
  4717.  
  4718. /***********************************************************************
  4719.  *
  4720.  * FUNCTION:    RecordViewAddSpaceForText
  4721.  *
  4722.  * DESCRIPTION: Adds space for text to the RecordViewLines info. 
  4723.  *
  4724.  * PARAMETERS:  string - CharPtr to text to leave space for
  4725.  *                width - width already occupied on the line
  4726.  *
  4727.  * RETURNED:    width is increased by the width of the text
  4728.  *
  4729.  * REVISION HISTORY:
  4730.  *         Name   Date      Description
  4731.  *         ----   ----      -----------
  4732.  *         roger   6/21/95   Initial Revision
  4733.  *
  4734.  ***********************************************************************/
  4735. static void RecordViewAddSpaceForText (const Char * const string, UInt *width)
  4736. {
  4737.    *width += FntCharsWidth(string, StrLen(string));
  4738. }
  4739.  
  4740.  
  4741. /***********************************************************************
  4742.  *
  4743.  * FUNCTION:    RecordViewPositionTextAt
  4744.  *
  4745.  * DESCRIPTION: Position the following text at the given position. 
  4746.  *
  4747.  * PARAMETERS:  position - position to indent to
  4748.  *                width - width already occupied on the line
  4749.  *
  4750.  * RETURNED:    width is increased if the position is greater
  4751.  *
  4752.  * REVISION HISTORY:
  4753.  *         Name   Date      Description
  4754.  *         ----   ----      -----------
  4755.  *         roger   8/2/95   Initial Revision
  4756.  *
  4757.  ***********************************************************************/
  4758. static void RecordViewPositionTextAt (UInt *width, const UInt position)
  4759. {
  4760.    if (*width < position)
  4761.       *width = position;
  4762. }
  4763.  
  4764.  
  4765. /***********************************************************************
  4766.  *
  4767.  * FUNCTION:    RecordViewAddField
  4768.  *
  4769.  * DESCRIPTION: Adds a field to the RecordViewLines info. 
  4770.  *
  4771.  * PARAMETERS:  fieldNum - field to add
  4772.  *                width - width already occupied on the line
  4773.  *                maxWidth - can't add words past this width
  4774.  *                indentation - the amounnt of indentation wrapped lines of
  4775.  *                              text should begin with (except the last)
  4776.  *
  4777.  * RETURNED:    width is set to the width of the last line added
  4778.  *
  4779.  * REVISION HISTORY:
  4780.  *         Name   Date      Description
  4781.  *         ----   ----      -----------
  4782.  *         roger   6/21/95   Initial Revision
  4783.  *
  4784.  ***********************************************************************/
  4785. static void RecordViewAddField (const UInt fieldNum, UInt *width, 
  4786.    const UInt maxWidth, const UInt indentation)
  4787. {
  4788.    UInt length;
  4789.    UInt offset = 0;
  4790.    UInt newOffset;
  4791.    
  4792.  
  4793.    if (recordViewRecord.fields[fieldNum] == NULL ||
  4794.       RecordViewLastLine >= recordViewLinesMax)
  4795.       return;
  4796.  
  4797.    // If we're past the maxWidth already then start at the beginning
  4798.    // of the next line.
  4799.    if (*width >= maxWidth)
  4800.       *width = indentation;
  4801.       
  4802.    do {
  4803.         if (RecordViewLastLine >= recordViewLinesMax)
  4804.             break;
  4805.       
  4806.         // Check if we word wrapped in the middle of a word which could 
  4807.         // fit on the next line.  Word wrap doesn't work well for use
  4808.         // when we call it twice on the same line.
  4809.         // The first part checks to see if we stopped in the middle of a line
  4810.         // The second part check to see if we didn't stop after a word break
  4811.         // The third part checks if this line wasn't a wide as it could be
  4812.         // because some other text used up space.
  4813.         length = FldWordWrap(&recordViewRecord.fields[fieldNum][offset], 
  4814.                             maxWidth - *width);
  4815.         if (recordViewRecord.fields[fieldNum][offset + length] != '\0'
  4816.             && !TxtCharIsSpace(recordViewRecord.fields[fieldNum][offset + length - 1])
  4817.             && (*width > indentation))
  4818.             {
  4819.             length = 0;            // don't word wrap - try next line
  4820.             }
  4821.  
  4822.         // Lines returned from FldWordWrap may include a '\n' at the
  4823.         // end.  If present remove it to keep it from being drawn.
  4824.         // The alternative is to not draw linefeeds at draw time.  That
  4825.         // seem more complex (there's many WinDrawChars) and slower as well.
  4826.         // This way is faster but makes catching word wrapping problems
  4827.         // less obvious (length 0 also happens when word wrap fails).
  4828.         newOffset = offset + length;
  4829.         if (newOffset > 0 && recordViewRecord.fields[fieldNum][newOffset - 1] == linefeedChr)
  4830.         length--;
  4831.         
  4832.         RecordViewLines[RecordViewLastLine].fieldNum = fieldNum;
  4833.         RecordViewLines[RecordViewLastLine].offset = offset;
  4834.         RecordViewLines[RecordViewLastLine].x = *width;
  4835.         RecordViewLines[RecordViewLastLine].length = length;
  4836.         RecordViewLastLine++;
  4837.         offset = newOffset;
  4838.             
  4839.         // Wrap to the start of the next line if there's still more text
  4840.         // to draw (so we must have run out of room) or wrap if we
  4841.         // encountered a line feed character.
  4842.         if (recordViewRecord.fields[fieldNum][offset] != '\0')
  4843.             *width = indentation;
  4844.         else
  4845.             break;
  4846.    } while (true);
  4847.  
  4848.  
  4849.    // If the last character was a new line then there is no width.
  4850.    // Otherwise the width is the width of the characters on the last line.
  4851.    if (recordViewRecord.fields[fieldNum][offset - 1] == linefeedChr)
  4852.       *width = 0;
  4853.    else
  4854.       *width += FntCharsWidth(&recordViewRecord.fields[fieldNum][
  4855.          RecordViewLines[RecordViewLastLine - 1].offset], 
  4856.          RecordViewLines[RecordViewLastLine - 1].length);
  4857. }
  4858.    
  4859.  
  4860. /***********************************************************************
  4861.  *
  4862.  * FUNCTION:    RecordViewInit
  4863.  *
  4864.  * DESCRIPTION: This routine initializes the "Record View" of the 
  4865.  *              Address application.  Most importantly it lays out the
  4866.  *                record and decides how the record is drawn.
  4867.  *
  4868.  * PARAMETERS:  frm - pointer to the view form.
  4869.  *
  4870.  * RETURNED:    true if the event has handle and should not be passed
  4871.  *              to a higher level handler.
  4872.  *
  4873.  * REVISION HISTORY:
  4874.  *         Name   Date      Description
  4875.  *         ----   ----      -----------
  4876.  *         roger   6/21/95   Initial Revision
  4877.  *
  4878.  ***********************************************************************/
  4879. static void RecordViewInit (FormPtr frm)
  4880. {
  4881.    UInt attr;
  4882.    Word index;
  4883.    Word category;
  4884.    AddrAppInfoPtr appInfoPtr;
  4885.    Handle RecordViewLinesH;
  4886.    UInt width = 0;
  4887.    UInt maxWidth;
  4888.    UInt winHeight;
  4889.    FontID curFont;
  4890.    UInt i;
  4891.    UInt fieldIndex;
  4892.    UInt phoneLabelNum;
  4893.  
  4894.  
  4895.    // Set the category label.
  4896.    if (CurrentCategory == dmAllCategories)
  4897.       {
  4898.       DmRecordInfo (AddrDB, CurrentRecord, &attr, NULL, NULL);   
  4899.       category = attr & dmRecAttrCategoryMask;
  4900.       }
  4901.    else 
  4902.       category = CurrentCategory;
  4903.  
  4904.    CategoryGetName (AddrDB, category, CategoryName);
  4905.    index = FrmGetObjectIndex (frm, RecordCategoryLabel);
  4906.    FrmSetCategoryLabel (frm, index, CategoryName);
  4907.  
  4908.  
  4909.  
  4910.    // Allocate the record view lines
  4911.    RecordViewLinesH = MemHandleNew(sizeof(RecordViewLineType) * recordViewLinesMax);
  4912.    ErrFatalDisplayIf (!RecordViewLinesH, "Out of memory");
  4913.    
  4914.    RecordViewLines = MemHandleLock(RecordViewLinesH);
  4915.    RecordViewLastLine = 0;
  4916.    TopRecordViewLine = 0;
  4917.    
  4918.    WinGetWindowExtent ((short *) &maxWidth, (short *) &winHeight);   // no wider than the window
  4919.    
  4920.    appInfoPtr = (AddrAppInfoPtr) AddrAppInfoGetPtr(AddrDB);
  4921.  
  4922.    if (RecordLabelColumnWidth == 0)
  4923.         RecordLabelColumnWidth = GetLabelColumnWidth (appInfoPtr, AddrRecordFont);
  4924.  
  4925.  
  4926.    // Get the record to display.  recordViewRecordH may have data if
  4927.    // we are redisplaying the record (custom fields changed).
  4928.    if (recordViewRecordH)
  4929.       MemHandleUnlock(recordViewRecordH);
  4930.    AddrGetRecord (AddrDB, CurrentRecord, &recordViewRecord, &recordViewRecordH);
  4931.    
  4932.    // Here we construct the recordViewLines info by laying out
  4933.    // the record 
  4934.    curFont = FntSetFont (largeBoldFont);
  4935.    if (recordViewRecord.fields[name] == NULL &&
  4936.       recordViewRecord.fields[firstName] == NULL)
  4937.       {
  4938.       RecordViewAddField(company, &width, maxWidth, 0);
  4939.       RecordViewNewLine(&width);
  4940.       }
  4941.    else
  4942.       {
  4943.       if (recordViewRecord.fields[firstName] != NULL)
  4944.          {
  4945.          RecordViewAddField(firstName, &width, maxWidth, 0);
  4946.          
  4947.          // Separate the last name from the first name as long
  4948.          // as they are together on the same line.
  4949.          if (width > 0)
  4950.             RecordViewAddSpaceForText (" ", &width);
  4951.          }
  4952.       RecordViewAddField(name, &width, maxWidth, 0);
  4953.       RecordViewNewLine(&width);
  4954.       }
  4955.    RecordViewFirstPlainLine = RecordViewLastLine;
  4956.     FntSetFont (AddrRecordFont);
  4957.  
  4958.    if (recordViewRecord.fields[title])
  4959.       {
  4960.       RecordViewAddField(title, &width, maxWidth, 0);
  4961.       RecordViewNewLine(&width);
  4962.       }
  4963.    if (recordViewRecord.fields[company] != NULL && 
  4964.       (recordViewRecord.fields[name] != NULL ||
  4965.       recordViewRecord.fields[firstName] != NULL))
  4966.       {
  4967.       RecordViewAddField(company, &width, maxWidth, 0);
  4968.       RecordViewNewLine(&width);
  4969.       }      
  4970.       
  4971.    // If the line above isn't blank then add a blank line
  4972.    if (RecordViewLastLine > 0 &&
  4973.       RecordViewLines[RecordViewLastLine - 1].fieldNum != recordViewBlankLine)
  4974.       {
  4975.       RecordViewNewLine(&width);
  4976.       }
  4977.  
  4978.  
  4979.  
  4980.    // Layout the phone numbers.  Start each number on its own line.
  4981.    // Put the label first, followed by ": " and then the number
  4982.    for (fieldIndex = firstPhoneField; fieldIndex <= lastPhoneField; fieldIndex++)
  4983.       {
  4984.       if (recordViewRecord.fields[fieldIndex])
  4985.          {
  4986.          phoneLabelNum = GetPhoneLabel(&recordViewRecord, fieldIndex);
  4987.          RecordViewAddSpaceForText (appInfoPtr->fieldLabels[
  4988.             phoneLabelNum + ((phoneLabelNum < numPhoneLabelsStoredFirst) ? 
  4989.             firstPhoneField : (addressFieldsCount - numPhoneLabelsStoredFirst))], 
  4990.             &width);
  4991.          RecordViewAddSpaceForText (": ", &width);
  4992.          RecordViewPositionTextAt(&width, RecordLabelColumnWidth);
  4993.          RecordViewAddField(fieldIndex, &width, maxWidth, RecordLabelColumnWidth);
  4994.          RecordViewNewLine(&width);
  4995.          }
  4996.       }
  4997.  
  4998.       
  4999.    // If the line above isn't blank then add a blank line
  5000.    if (RecordViewLastLine > 0 &&
  5001.       RecordViewLines[RecordViewLastLine - 1].fieldNum != recordViewBlankLine)
  5002.       {
  5003.       RecordViewNewLine(&width);
  5004.       }
  5005.       
  5006.  
  5007.  
  5008.    // Now do the address information
  5009.    if (recordViewRecord.fields[address])
  5010.       {
  5011.       RecordViewAddField(address, &width, maxWidth, 0);
  5012.       RecordViewNewLine(&width);
  5013.       }
  5014.  
  5015.    
  5016. #if COUNTRY == COUNTRY_UNITED_STATES
  5017.  
  5018.    if (recordViewRecord.fields[city])
  5019.       {
  5020.       RecordViewAddField(city, &width, maxWidth, 0);
  5021.       }
  5022.    if (recordViewRecord.fields[state])
  5023.       {
  5024.       if (width > 0)
  5025.          RecordViewAddSpaceForText (", ", &width);
  5026.       RecordViewAddField(state, &width, maxWidth, 0);
  5027.       }
  5028.    if (recordViewRecord.fields[zipCode])
  5029.       {
  5030.       if (width > 0)
  5031.          RecordViewAddSpaceForText ("   ", &width);
  5032.       RecordViewAddField(zipCode, &width, maxWidth, 0);
  5033.       }
  5034.    if (recordViewRecord.fields[city] ||
  5035.       recordViewRecord.fields[state] ||
  5036.       recordViewRecord.fields[zipCode])
  5037.       {
  5038.       RecordViewNewLine(&width);
  5039.       }
  5040.  
  5041. #else      // COUNTRY_FRANCE or COUNTRY_GERMANY
  5042.  
  5043.    if (recordViewRecord.fields[zipCode])
  5044.       {
  5045.       RecordViewAddField(zipCode, &width, maxWidth, 0);
  5046.       }
  5047.    if (recordViewRecord.fields[city])
  5048.       {
  5049.       if (width > 0)
  5050.          RecordViewAddSpaceForText (" ", &width);
  5051.       RecordViewAddField(city, &width, maxWidth, 0);
  5052.       }
  5053.    if (recordViewRecord.fields[zipCode] ||
  5054.       recordViewRecord.fields[city])
  5055.       {
  5056.       RecordViewNewLine(&width);
  5057.       }
  5058.    if (recordViewRecord.fields[state])
  5059.       {
  5060.       RecordViewAddField(state, &width, maxWidth, 0);
  5061.       RecordViewNewLine(&width);
  5062.       }
  5063. #endif
  5064.  
  5065.    if (recordViewRecord.fields[country])
  5066.       {
  5067.       RecordViewAddField(country, &width, maxWidth, 0);
  5068.       RecordViewNewLine(&width);
  5069.       }
  5070.  
  5071.  
  5072.    // If the line above isn't blank then add a blank line
  5073.    if (RecordViewLastLine > 0 &&
  5074.       RecordViewLines[RecordViewLastLine - 1].fieldNum != recordViewBlankLine)
  5075.       {
  5076.       RecordViewNewLine(&width);
  5077.       }
  5078.  
  5079.  
  5080.    // Do the custom fields
  5081.    for (i = custom1; i < addressFieldsCount - 1; i++)
  5082.       {
  5083.       if (recordViewRecord.fields[i])
  5084.          {
  5085.          RecordViewAddSpaceForText (appInfoPtr->fieldLabels[i], &width);
  5086.          RecordViewAddSpaceForText (": ", &width);
  5087.          RecordViewPositionTextAt(&width, RecordLabelColumnWidth);
  5088.          RecordViewAddField(i, &width, maxWidth, RecordLabelColumnWidth);
  5089.          RecordViewNewLine(&width);
  5090.          RecordViewNewLine(&width);      // leave a blank line
  5091.          }
  5092.       }
  5093.             
  5094.    // Show the note field.
  5095.    if (recordViewRecord.fields[note])
  5096.       {
  5097.       RecordViewAddField(note, &width, maxWidth, 0);
  5098.       }
  5099.    
  5100.  
  5101.    // Now remove trailing blank lines
  5102.    while (RecordViewLastLine > 0 &&
  5103.       RecordViewLines[RecordViewLastLine - 1].fieldNum == recordViewBlankLine)
  5104.       {
  5105.       RecordViewLastLine--;
  5106.       }
  5107.  
  5108.  
  5109.    MemPtrUnlock(appInfoPtr);
  5110.    FntSetFont (curFont);
  5111. }
  5112.    
  5113.  
  5114. /***********************************************************************
  5115.  *
  5116.  * FUNCTION:    RecordViewErase
  5117.  *
  5118.  * DESCRIPTION: Erases the record view
  5119.  *
  5120.  * PARAMETERS:  nothing
  5121.  *
  5122.  * RETURNED:    true if the event has handle and should not be passed
  5123.  *              to a higher level handler.
  5124.  *
  5125.  * REVISION HISTORY:
  5126.  *         Name   Date      Description
  5127.  *         ----   ----      -----------
  5128.  *         roger   6/30/95   Initial Revision
  5129.  *
  5130.  ***********************************************************************/
  5131. static void RecordViewErase ()
  5132. {
  5133.    FormPtr frm;
  5134.    RectangleType r;
  5135.  
  5136.  
  5137.    frm = FrmGetActiveForm();
  5138.    FrmGetObjectBounds(frm, FrmGetObjectIndex(frm, RecordViewDisplay), &r);
  5139.    WinEraseRectangle (&r, 0);
  5140. }
  5141.    
  5142.  
  5143. /***********************************************************************
  5144.  *
  5145.  * FUNCTION:    RecordViewCalcNextLine
  5146.  *
  5147.  * DESCRIPTION: Calculate how far to advance to the next line.
  5148.  * A Blank line or text which begins to the left of text on the
  5149.  * current line advance the line down.  Multiple blank lines in
  5150.  * succession advance the line down half a line at a time.
  5151.  *
  5152.  * PARAMETERS:  i - the line to base how far to advance
  5153.  *                oneLine - the amount which advance one line down.
  5154.  *
  5155.  * RETURNED:    the amount to advance.  Typically oneLine or oneLine / 2.
  5156.  *
  5157.  * REVISION HISTORY:
  5158.  *         Name   Date      Description
  5159.  *         ----   ----      -----------
  5160.  *         roger   9/28/95   Initial Revision
  5161.  *
  5162.  ***********************************************************************/
  5163. static UInt   RecordViewCalcNextLine(UInt i, UInt oneLine)
  5164. {
  5165.    // Advance down if the text starts before the text of the current line.
  5166.    if (RecordViewLines[i].x == 0 || 
  5167.       (i > 0 && 
  5168.          (RecordViewLines[i].x <= RecordViewLines[i - 1].x ||
  5169.          RecordViewLines[i - 1].fieldNum == recordViewBlankLine)))
  5170.       {
  5171.       // A non blank line moves down a full line.
  5172.       if (RecordViewLines[i].fieldNum != recordViewBlankLine)
  5173.          {
  5174.          return oneLine;
  5175.          }   
  5176.       else
  5177.          {
  5178.          // A recordViewBlankLine followed by another recordViewBlankLine
  5179.          // only moves down half a line to conserve vertical screen space.
  5180. /*         if (i < RecordViewLastLine && RecordViewLines[i + 1].x > 0)
  5181.             return oneLine;
  5182.          else
  5183. */
  5184.          // A recordViewBlankLine is half-height.
  5185.          return oneLine / 2;
  5186.          }
  5187.       }
  5188.    return 0;      // Stay on the same line.
  5189. }
  5190.    
  5191.  
  5192. /***********************************************************************
  5193.  *
  5194.  * FUNCTION:    RecordViewDrawSelectedText
  5195.  *
  5196.  * DESCRIPTION: Inverts text which is considered selected.
  5197.  *
  5198.  * PARAMETERS:  currentField - field containing the selected text
  5199.  *                selectPos - offset into field for start of selected text
  5200.  *                selectLen - length of selected text.  This field
  5201.  *                  should be zero if selected text isn't desired.
  5202.  *                textY - where on the screen the text was drawn
  5203.  *
  5204.  * RETURNED:    nothing
  5205.  *
  5206.  * REVISION HISTORY:
  5207.  *         Name   Date      Description
  5208.  *         ----   ----      -----------
  5209.  *         roger   11/27/95   Cut from RecordViewDraw
  5210.  *
  5211.  ***********************************************************************/
  5212. static void RecordViewDrawSelectedText (UInt currentField, 
  5213.    UInt selectPos, UInt selectLen, UInt textY)
  5214. {
  5215.    UInt selectXLeft = 0;
  5216.    UInt selectXRight = 0;
  5217.    RectangleType invertRect;
  5218.  
  5219.  
  5220.    // If the start of the selected region is on this line calc an x
  5221.    if (RecordViewLines[currentField].offset <= selectPos && 
  5222.       selectPos < RecordViewLines[currentField].offset + 
  5223.          RecordViewLines[currentField].length)
  5224.       {
  5225.       selectXLeft = FntCharsWidth(&recordViewRecord.fields[
  5226.          RecordViewLines[currentField].fieldNum][
  5227.             RecordViewLines[currentField].offset], 
  5228.          selectPos - RecordViewLines[currentField].offset);
  5229.       }
  5230.    // If the end of the selected region is on this line calc an x
  5231.    if (RecordViewLines[currentField].offset <= selectPos + selectLen && 
  5232.       selectPos + selectLen <= RecordViewLines[currentField].offset + 
  5233.       RecordViewLines[currentField].length)
  5234.       {
  5235.       selectXRight = FntCharsWidth(&recordViewRecord.fields[
  5236.          RecordViewLines[currentField].fieldNum][
  5237.          RecordViewLines[currentField].offset], 
  5238.          selectPos + selectLen - RecordViewLines[currentField].offset);
  5239.       }
  5240.    
  5241.    // If either the left or right have been set then some
  5242.    // text needs to be selected.
  5243.    if (selectXLeft | selectXRight)
  5244.       {
  5245.       if (!selectXRight)
  5246.          selectXRight = RecordViewLines[currentField].x + 
  5247.             FntCharsWidth(&recordViewRecord.fields[
  5248.             RecordViewLines[currentField].fieldNum][
  5249.             RecordViewLines[currentField].offset], 
  5250.             RecordViewLines[currentField].length);
  5251.       // Now add in the left x of the text
  5252.       selectXLeft += RecordViewLines[currentField].x;
  5253.       selectXRight += RecordViewLines[currentField].x;
  5254.       
  5255.       // When hightlighting the text start the highlight one pixel to the left of the
  5256.       // text so the left edge of the inverted area isn't ragged.  If the text is 
  5257.       // at the far left we obviously can't go left more.
  5258.       if (selectXLeft > 0)
  5259.          selectXLeft--;
  5260.       
  5261.       // Invert the text
  5262.       invertRect.topLeft.x = selectXLeft;
  5263.       invertRect.extent.x = selectXRight - selectXLeft;
  5264.       invertRect.topLeft.y = textY;
  5265.       invertRect.extent.y = FntLineHeight();
  5266.       WinInvertRectangle (&invertRect, 0);
  5267.       
  5268.       // Reset incase text needs inversion on the next line
  5269.       selectXLeft = 0;
  5270.       selectXRight = 0;
  5271.       }
  5272. }
  5273.  
  5274.  
  5275. /***********************************************************************
  5276.  *
  5277.  * FUNCTION:    RecordViewDraw
  5278.  *
  5279.  * DESCRIPTION: This routine initializes the "Record View"
  5280.  *
  5281.  * PARAMETERS:  selectFieldNum - field to show selected text
  5282.  *                selectPos - offset into field for start of selected text
  5283.  *                selectLen - length of selected text.  This field
  5284.  *                  should be zero if selected text isn't desired.
  5285.  *
  5286.  * RETURNED:    true if the event has handle and should not be passed
  5287.  *              to a higher level handler.
  5288.  *
  5289.  * REVISION HISTORY:
  5290.  *         Name   Date      Description
  5291.  *         ----   ----      -----------
  5292.  *         roger   6/21/95   Initial Revision
  5293.  *         tlw       2/6/98    Change test below "If we are past bottom stop drawing" from
  5294.  *                             if >= bottomOfRecordViewDisplay - FntLineHeight() to
  5295.  *                             if > bottomOfRecordViewDisplay - FntLineHeight()
  5296.  *                             to allow last line to be drawn.
  5297.  *
  5298.  ***********************************************************************/
  5299. static void RecordViewDraw (UInt selectFieldNum, UInt selectPos,
  5300.    UInt selectLen)
  5301. {
  5302.    AddrAppInfoPtr appInfoPtr;
  5303.    UInt y;
  5304.    UInt i;
  5305.    FontID curFont;
  5306.    FormPtr   frm;
  5307.    UInt phoneLabelNum;
  5308.    CharPtr fieldLabelString;
  5309.    UInt fieldLabelLength;
  5310.    Word upIndex;
  5311.    Word downIndex;
  5312.    Boolean scrollableUp;
  5313.    Boolean scrollableDown;
  5314.    RectangleType r;
  5315.    int bottomOfRecordViewDisplay;
  5316.  
  5317.  
  5318.    appInfoPtr = (AddrAppInfoPtr) AddrAppInfoGetPtr(AddrDB);
  5319.  
  5320.  
  5321.    frm = FrmGetActiveForm();
  5322.    FrmGetObjectBounds(frm, FrmGetObjectIndex(frm, RecordViewDisplay), &r);
  5323.    bottomOfRecordViewDisplay = r.topLeft.y +  r.extent.y;
  5324.    
  5325.    if (TopRecordViewLine < RecordViewFirstPlainLine)
  5326.       curFont = FntSetFont (largeBoldFont);
  5327.    else
  5328.         curFont = FntSetFont (AddrRecordFont);
  5329.  
  5330.    y = r.topLeft.y - RecordViewCalcNextLine(TopRecordViewLine, FntLineHeight());
  5331.    
  5332.    for (i = TopRecordViewLine; i < RecordViewLastLine; i++)
  5333.       {
  5334.       
  5335.       // This must be done before the font shrinks or else
  5336.       // we move down less to the next row and overwrite the 
  5337.       // descenders of the last row which used a large font.
  5338.       y += RecordViewCalcNextLine(i, FntLineHeight());
  5339.       
  5340.       if (i == RecordViewFirstPlainLine)
  5341.             FntSetFont (AddrRecordFont);
  5342.  
  5343.       // If we are past the bottom stop drawing
  5344.       if (y > bottomOfRecordViewDisplay - FntLineHeight())
  5345.          break;
  5346.       
  5347.       ErrNonFatalDisplayIf(y < r.topLeft.y, "Drawing record out of gadget");
  5348.          
  5349.  
  5350.  
  5351.       if (RecordViewLines[i].offset == 0)
  5352.          {
  5353.          switch (RecordViewLines[i].fieldNum)
  5354.             {
  5355.             case recordViewBlankLine:
  5356.                break;
  5357.    
  5358.    
  5359.             case phone1:
  5360.             case phone2:
  5361.             case phone3:
  5362.             case phone4:
  5363.             case phone5:
  5364.                if (RecordViewLines[i].offset == 0)
  5365.                   {
  5366.                   phoneLabelNum = GetPhoneLabel(&recordViewRecord, RecordViewLines[i].fieldNum);
  5367.                   fieldLabelString = appInfoPtr->fieldLabels[phoneLabelNum + 
  5368.                      ((phoneLabelNum < numPhoneLabelsStoredFirst) ? 
  5369.                      firstPhoneField : (addressFieldsCount - numPhoneLabelsStoredFirst))];
  5370.                   fieldLabelLength = StrLen(fieldLabelString);
  5371.                   WinDrawChars(fieldLabelString, fieldLabelLength, 0, y);
  5372.                   WinDrawChars(": ", 2, FntCharsWidth(fieldLabelString, fieldLabelLength), y);
  5373.                   }
  5374.                WinDrawChars(&recordViewRecord.fields[
  5375.                   RecordViewLines[i].fieldNum][RecordViewLines[i].offset],
  5376.                   RecordViewLines[i].length, RecordViewLines[i].x, y);
  5377.                break;
  5378.                
  5379.                
  5380.             case custom1:
  5381.             case custom2:
  5382.             case custom3:
  5383.             case custom4:
  5384.                fieldLabelString = appInfoPtr->fieldLabels[RecordViewLines[i].fieldNum];
  5385.                fieldLabelLength = StrLen(fieldLabelString);
  5386.                if (RecordViewLines[i].length == 0
  5387.                        || FntCharsWidth(fieldLabelString, fieldLabelLength) < RecordViewLines[i].x)
  5388.                         {
  5389.                   WinDrawChars(fieldLabelString, fieldLabelLength, 0, y);
  5390.                   WinDrawChars(": ", 2, FntCharsWidth(fieldLabelString, fieldLabelLength), y);
  5391.                   }
  5392.                WinDrawChars(&recordViewRecord.fields[
  5393.                   RecordViewLines[i].fieldNum][RecordViewLines[i].offset],
  5394.                   RecordViewLines[i].length, RecordViewLines[i].x, y);
  5395.                break;
  5396.                
  5397.  
  5398.             case state:
  5399.                if (RecordViewLines[i].x > 0)
  5400.                   WinDrawChars(", ", 2, RecordViewLines[i].x - FntCharsWidth(", ", 2), y);
  5401.                WinDrawChars(&recordViewRecord.fields[
  5402.                   RecordViewLines[i].fieldNum][RecordViewLines[i].offset],
  5403.                   RecordViewLines[i].length, RecordViewLines[i].x, y);
  5404.                break;
  5405.                
  5406.             case zipCode:
  5407. #if COUNTRY == COUNTRY_UNITED_STATES
  5408.                if (RecordViewLines[i].x > 0)
  5409.                   WinDrawChars("   ", 3, RecordViewLines[i].x - FntCharsWidth("   ", 3), y);
  5410. #else      // COUNTRY_FRANCE or COUNTRY_GERMANY
  5411.                if (RecordViewLines[i].x > 0)
  5412.                   WinDrawChars(" ", 1, RecordViewLines[i].x - FntCharsWidth(" ", 1), y);
  5413. #endif
  5414.                WinDrawChars(&recordViewRecord.fields[
  5415.                   RecordViewLines[i].fieldNum][RecordViewLines[i].offset],
  5416.                   RecordViewLines[i].length, RecordViewLines[i].x, y);
  5417.                break;
  5418.                
  5419.             default:
  5420.                WinDrawChars(&recordViewRecord.fields[
  5421.                   RecordViewLines[i].fieldNum][RecordViewLines[i].offset],
  5422.                   RecordViewLines[i].length, RecordViewLines[i].x, y);
  5423.                break;
  5424.             }
  5425.          }
  5426.       else
  5427.          {
  5428.          // Draw the remainder of the fields' lines without any 
  5429.          // other special handling.
  5430.          if (RecordViewLines[i].fieldNum != recordViewBlankLine)
  5431.             {
  5432.             WinDrawChars(&recordViewRecord.fields[
  5433.                RecordViewLines[i].fieldNum][RecordViewLines[i].offset],
  5434.                RecordViewLines[i].length, RecordViewLines[i].x, y);
  5435.             }
  5436.          }
  5437.  
  5438.       
  5439.       // Highlight text if it is within the selection bounds.  This is
  5440.       // used to select found text.
  5441.       if (RecordViewLines[i].fieldNum == selectFieldNum &&
  5442.          selectLen > 0)
  5443.          {
  5444.          RecordViewDrawSelectedText (i, selectPos, selectLen, y);
  5445.          }
  5446.       }
  5447.                
  5448.             
  5449.    MemPtrUnlock(appInfoPtr);
  5450.    FntSetFont (curFont);
  5451.    
  5452.    
  5453.    // Now show/hide the scroll arrows
  5454.    frm = FrmGetActiveForm ();
  5455.  
  5456.    scrollableUp = TopRecordViewLine != 0;
  5457.    scrollableDown = i < RecordViewLastLine; 
  5458.  
  5459.  
  5460.    // Update the scroll button.
  5461.    upIndex = FrmGetObjectIndex (frm, RecordUpButton);
  5462.    downIndex = FrmGetObjectIndex (frm, RecordDownButton);
  5463.    FrmUpdateScrollers (frm, upIndex, downIndex, scrollableUp, scrollableDown);
  5464. }
  5465.  
  5466.  
  5467. /***********************************************************************
  5468.  *
  5469.  * FUNCTION:    RecordViewDrawBusinessCardIndicator
  5470.  *
  5471.  * DESCRIPTION: Draw the business card indicator if the current record is
  5472.  * the business card.
  5473.  *
  5474.  * PARAMETERS:  formP - the form containing the business card indicator
  5475.  *
  5476.  * RETURNED:    nothing
  5477.  *
  5478.  * REVISION HISTORY:
  5479.  *         Name   Date      Description
  5480.  *         ----   ----      -----------
  5481.  *         roger  10/22/97  Initial Revision
  5482.  *
  5483.  ***********************************************************************/
  5484. static void RecordViewDrawBusinessCardIndicator (FormPtr formP)
  5485. {
  5486.     DWord uniqueID;
  5487.     
  5488.     DmRecordInfo (AddrDB, CurrentRecord, NULL, &uniqueID, NULL);
  5489.     if (BusinessCardRecordID == uniqueID)
  5490.         FrmShowObject(formP, FrmGetObjectIndex (formP, RecordViewBusinessCardBmp));
  5491.     else
  5492.         FrmHideObject(formP, FrmGetObjectIndex (formP, RecordViewBusinessCardBmp));
  5493.     
  5494. }   
  5495.  
  5496.  
  5497. /***********************************************************************
  5498.  *
  5499.  * FUNCTION:    RecordViewUpdate
  5500.  *
  5501.  * DESCRIPTION: Update the record view and redraw it.
  5502.  *
  5503.  * PARAMETERS:  nothing
  5504.  *
  5505.  * RETURNED:    nothing
  5506.  *
  5507.  * REVISION HISTORY:
  5508.  *         Name   Date      Description
  5509.  *         ----   ----      -----------
  5510.  *         roger   10/18/95   Initial Revision
  5511.  *
  5512.  ***********************************************************************/
  5513. static void RecordViewUpdate ()
  5514. {
  5515.    FormPtr frm;
  5516.    
  5517.    
  5518.    MemHandleFree(MemPtrRecoverHandle(RecordViewLines));
  5519.    RecordViewLines = 0;
  5520.    frm = FrmGetActiveForm ();
  5521.    RecordViewInit (frm);
  5522.    RecordViewErase ();
  5523.    RecordViewDraw(0, 0, 0);
  5524.    RecordViewDrawBusinessCardIndicator(frm);
  5525. }
  5526.  
  5527.  
  5528. /***********************************************************************
  5529.  *
  5530.  * FUNCTION:    RecordViewScrollOnePage
  5531.  *
  5532.  * DESCRIPTION: Scrolls the record view by one page less one line unless
  5533.  * we scroll from RecordViewLastLine (used by scroll code).
  5534.  *
  5535.  * PARAMETERS:  newTopRecordViewLine - top line of the display
  5536.  *              direction - up or dowm
  5537.  *
  5538.  * RETURNED:    new newTopRecordViewLine one page away
  5539.  *
  5540.  * REVISION HISTORY:
  5541.  *         Name   Date      Description
  5542.  *         ----   ----      -----------
  5543.  *         roger   6/22/95   Initial Revision
  5544.  *         roger 8/2/95   Reworked to handle half height blank lines.
  5545.  *         roger 10/30/95   Reworked to obey FntLineHeight
  5546.  *         roger 10/31/95   Broke out of RecordViewScroll
  5547.  *
  5548.  ***********************************************************************/
  5549. static UInt RecordViewScrollOnePage (UInt newTopRecordViewLine, 
  5550.    DirectionType direction)
  5551. {
  5552.    Int offset;
  5553.    FontID curFont;
  5554.    FormPtr frm;
  5555.    Int largeFontLineHeight;
  5556.    Int stdFontLineHeight;
  5557.    Int currentLineHeight;
  5558.    RectangleType r;
  5559.    Int recordViewDisplayHeight;
  5560.  
  5561.  
  5562.    // setup stuff
  5563.    curFont = FntSetFont (largeBoldFont);
  5564.    largeFontLineHeight = FntLineHeight();
  5565.     FntSetFont (AddrRecordFont);
  5566.    stdFontLineHeight = FntLineHeight();
  5567.    FntSetFont (curFont);
  5568.    
  5569.    frm = FrmGetActiveForm();
  5570.    FrmGetObjectBounds(frm, FrmGetObjectIndex(frm, RecordViewDisplay), &r);
  5571.    recordViewDisplayHeight = r.extent.y;
  5572.    if (newTopRecordViewLine != RecordViewLastLine)
  5573.       recordViewDisplayHeight -= stdFontLineHeight;   // less one one line
  5574.    
  5575.  
  5576.    if (direction == up)
  5577.       offset = -1;
  5578.    else
  5579.       offset = 1;
  5580.    
  5581.  
  5582.    while (recordViewDisplayHeight >= 0 && 
  5583.       (newTopRecordViewLine > 0 || direction == down) && 
  5584.       (newTopRecordViewLine < (RecordViewLastLine - 1) || direction == up)) 
  5585.       {
  5586.       newTopRecordViewLine += offset;
  5587.       if (RecordViewLines[newTopRecordViewLine].fieldNum <= firstName)
  5588.          currentLineHeight = largeFontLineHeight;
  5589.       else
  5590.          currentLineHeight = stdFontLineHeight;
  5591.  
  5592.       recordViewDisplayHeight -= RecordViewCalcNextLine(newTopRecordViewLine, 
  5593.          currentLineHeight);
  5594.       };
  5595.       
  5596.    // Did we go too far?
  5597.    if (recordViewDisplayHeight < 0)
  5598.       {
  5599.       // The last line was too much so remove it
  5600.       newTopRecordViewLine -= offset;
  5601.       
  5602.       // Also remove any lines which don't have a height
  5603.       while (RecordViewCalcNextLine(newTopRecordViewLine, 2) == 0)
  5604.          {
  5605.          newTopRecordViewLine -= offset;   // skip it
  5606.          }
  5607.       }
  5608.    
  5609.    return newTopRecordViewLine;
  5610. }
  5611.    
  5612.  
  5613. /***********************************************************************
  5614.  *
  5615.  * FUNCTION:    RecordViewScroll
  5616.  *
  5617.  * DESCRIPTION: Scrolls the record view
  5618.  *
  5619.  * PARAMETERS:  direction - up or dowm
  5620.  *
  5621.  * RETURNED:    true if the event has handle and should not be passed
  5622.  *              to a higher level handler.
  5623.  *
  5624.  * REVISION HISTORY:
  5625.  *         Name   Date      Description
  5626.  *         ----   ----      -----------
  5627.  *         roger   6/22/95   Initial Revision
  5628.  *         roger 8/2/95   Reworked to handle half height blank lines.
  5629.  *         roger 10/30/95   Reworked to obey FntLineHeight
  5630.  *
  5631.  ***********************************************************************/
  5632. static void RecordViewScroll (DirectionType direction)
  5633. {
  5634.    Int lastRecordViewLine;
  5635.    UInt newTopRecordViewLine;
  5636.    Word category;
  5637.    UInt recordNum;
  5638.    Int seekDirection;
  5639.  
  5640.  
  5641.    newTopRecordViewLine = TopRecordViewLine;
  5642.     if (direction == up)
  5643.         {
  5644.         newTopRecordViewLine = RecordViewScrollOnePage (newTopRecordViewLine, direction);
  5645.         }
  5646.    else
  5647.         {
  5648.         // Simple two part algorithm.
  5649.         // 1) Scroll down one page
  5650.         // 2) Scroll up one page from the bottom
  5651.         // Use the higher of the two positions
  5652.         // Find the line one page down
  5653.  
  5654.         newTopRecordViewLine = RecordViewScrollOnePage (newTopRecordViewLine, direction);
  5655.  
  5656.         // Find the line at the top of the last page 
  5657.         // (code copied to RecordViewMakeVisible).
  5658.         lastRecordViewLine = RecordViewScrollOnePage (RecordViewLastLine, up);
  5659.  
  5660.  
  5661.         // We shouldn't be past the top line of the last page
  5662.         if (newTopRecordViewLine > lastRecordViewLine)
  5663.         newTopRecordViewLine = lastRecordViewLine;
  5664.  
  5665.         }
  5666.  
  5667.  
  5668.    if (newTopRecordViewLine != TopRecordViewLine)
  5669.       {
  5670.       TopRecordViewLine = newTopRecordViewLine;
  5671.       
  5672.       RecordViewErase ();
  5673.       RecordViewDraw(0, 0, 0);
  5674.       }
  5675.  
  5676.    // If we couldn't scroll then scroll to the next record.
  5677.    else
  5678.       {
  5679.       // Move to the next or previous memo.
  5680.       if (direction == up)
  5681.          {
  5682.          seekDirection = dmSeekBackward;
  5683.          }
  5684.       else
  5685.          {
  5686.          seekDirection = dmSeekForward;
  5687.          }
  5688.    
  5689.       if (ShowAllCategories)
  5690.          category = dmAllCategories;
  5691.       else
  5692.          category = CurrentCategory;
  5693.    
  5694.       recordNum = CurrentRecord;
  5695.       DmSeekRecordInCategory (AddrDB, &recordNum, 1, seekDirection, category);
  5696.       if (recordNum == CurrentRecord) return;
  5697.    
  5698.       SndPlaySystemSound (sndInfo);
  5699.    
  5700.       CurrentRecord = recordNum;
  5701.       RecordViewUpdate ();
  5702.       }
  5703.    
  5704. }
  5705.    
  5706.  
  5707. /***********************************************************************
  5708.  *
  5709.  * FUNCTION:    RecordViewMakeVisible
  5710.  *
  5711.  * DESCRIPTION: Make a selection range visible 
  5712.  *
  5713.  * PARAMETERS:  selectFieldNum - field to show selected text
  5714.  *                selectPos - offset into field for start of selected text
  5715.  *                selectLen - length of selected text
  5716.  *
  5717.  * RETURNED:    nothing
  5718.  *
  5719.  * REVISION HISTORY:
  5720.  *         Name   Date      Description
  5721.  *         ----   ----      -----------
  5722.  *         roger   8/3/95   Initial Revision
  5723.  *
  5724.  ***********************************************************************/
  5725. static void RecordViewMakeVisible (UInt selectFieldNum, UInt selectPos,
  5726.    UInt selectLen)
  5727. {
  5728.    UInt newTopRecordViewLine;
  5729.    UInt i;
  5730.  
  5731.  
  5732.    newTopRecordViewLine = RecordViewLastLine;
  5733.    for (i = 0; i < RecordViewLastLine; i++)
  5734.       {
  5735.       // Does the selected range end here?
  5736.       if (RecordViewLines[i].fieldNum == selectFieldNum &&
  5737.          RecordViewLines[i].offset <= selectPos + selectLen && 
  5738.          selectPos + selectLen <= RecordViewLines[i].offset + 
  5739.             RecordViewLines[i].length)
  5740.          {
  5741.          newTopRecordViewLine = i;
  5742.          }
  5743.       }
  5744.  
  5745.  
  5746.    // If the selected range doesn't seem to exist then
  5747.    // we shouldn't scroll the view.
  5748.    if (newTopRecordViewLine == RecordViewLastLine)
  5749.       return;
  5750.  
  5751.  
  5752.    // Display as much before the selected text as possible
  5753.    newTopRecordViewLine = RecordViewScrollOnePage (newTopRecordViewLine, up);
  5754.  
  5755.    if (newTopRecordViewLine != TopRecordViewLine)
  5756.       TopRecordViewLine = newTopRecordViewLine;
  5757. }
  5758.  
  5759. static    void    DialHumanPhoneNumber( CharPtr humanString, UInt humanStringSize )
  5760. {
  5761.     char    strippedNumber[ 11 ];
  5762.     UInt    strippedNumberEnd = 0;
  5763.     UInt    i;
  5764.     char    areaCode[ 4 ];
  5765.     char    local[ 8 ];
  5766.     Word    btn;
  5767.     char    dial[ 12 ];
  5768.     Err        err;
  5769.     UInt16    refNum;
  5770.     
  5771.     //    Strip out everything but the numbers.
  5772.     for( i = 0; i <= humanStringSize && strippedNumberEnd < 11; ++i ) {
  5773.         if( humanString[ i ] == 'x' )
  5774.             break;
  5775.         if( humanString[ i ] >= '0' && humanString[ i ] <= '9' )
  5776.             strippedNumber[ strippedNumberEnd++ ] = humanString[ i ];
  5777.     }
  5778.     
  5779.     //    If there is an area code, pull it out and ask the user if he wants to
  5780.     //    dial it.
  5781.     if( strippedNumberEnd > 7 ) {
  5782.         if( strippedNumber[ 0 ] == '1' ) {
  5783.             StrNCopy( areaCode, &strippedNumber[ 1 ], 3 );
  5784.             StrNCopy( local, &strippedNumber[ 4 ], strippedNumberEnd-4 );
  5785.         } else {
  5786.             StrNCopy( areaCode, strippedNumber, 3 );
  5787.             StrNCopy( local, &strippedNumber[ 3 ], strippedNumberEnd-3 );
  5788.         }
  5789.         areaCode[ 3 ] = 0;
  5790.         btn = FrmCustomAlert( DialAreaCodeAlert, areaCode, NULL, NULL );
  5791.     } else {
  5792.         StrNCopy( local, strippedNumber, strippedNumberEnd );
  5793.     }
  5794.     local[ 7 ] = 0;
  5795.     
  5796.     //    Create a string representing the number to dial.
  5797.     dial[ 0 ] = 0;
  5798.     if( strippedNumberEnd > 7 && btn == DialAreaCodeAlertYes ) {
  5799.         StrNCat( dial, "1", 11 );
  5800.         StrNCat( dial, areaCode, 11 );
  5801.     }
  5802.     StrNCat( dial, local, 11 );
  5803.     
  5804.     //    Load the audio library, dial the number and unload the library.
  5805.     err = SysLibFind( AudioLibName, &refNum );
  5806.     if( err ) {
  5807.         err = SysLibLoad( AudioLibTypeID, AudioLibCreatorID, &refNum );
  5808.     }
  5809.     if( refNum != sysInvalidRefNum ) {
  5810.         err = AudioLibOpen( refNum );
  5811.         if( err ) {
  5812.             SysLibRemove( refNum );
  5813.         } else {
  5814.             AudioPlayDTMFStr( refNum, dial, 5, 1 );
  5815.             AudioLibClose( refNum );
  5816.             SysLibRemove( refNum );
  5817.         }
  5818.     }
  5819. }
  5820.  
  5821. /***********************************************************************
  5822.  *
  5823.  * FUNCTION:    RecordViewHandlePen
  5824.  *
  5825.  * DESCRIPTION: Handle pen movement in the RecordViewDisplay. 
  5826.  *
  5827.  * PARAMETERS:  event  - a pointer to an EventType structure
  5828.  *
  5829.  * RETURNED:    true if handled.
  5830.  *
  5831.  * REVISION HISTORY:
  5832.  *         Name   Date      Description
  5833.  *         ----   ----      -----------
  5834.  *         roger   11/27/95   Cut from RecordViewHandleEvent
  5835.  *
  5836.  ***********************************************************************/
  5837. static Boolean RecordViewHandlePen (EventPtr event)
  5838. {
  5839.     FormPtr            frm;
  5840.     RectangleType    r;
  5841.     short            x, y;
  5842.     Boolean            penDown, dialed = false, handled = false;
  5843.     
  5844.     frm = FrmGetActiveForm();
  5845.     FrmGetObjectBounds( frm, FrmGetObjectIndex( frm, RecordViewDisplay ), &r );
  5846.     if( RctPtInRectangle( event->screenX, event->screenY, &r ) ) {
  5847.         do {
  5848.             PenGetPoint( &x, &y, &penDown );
  5849.         } while( penDown );
  5850.         
  5851.         if( RctPtInRectangle( x, y, &r ) ) {
  5852.             int        bottomOfRecordViewDisplay;
  5853.             FontID    curFont;
  5854.             UInt    y2, i;
  5855.             
  5856.             bottomOfRecordViewDisplay = r.topLeft.y + r.extent.y;
  5857.             if( TopRecordViewLine < RecordViewFirstPlainLine )
  5858.                 curFont = FntSetFont( largeBoldFont );
  5859.             else
  5860.                 curFont = FntSetFont( AddrRecordFont );
  5861.             
  5862.             y2 = r.topLeft.y - RecordViewCalcNextLine( TopRecordViewLine, FntLineHeight() );
  5863.             
  5864.             for( i = TopRecordViewLine; i < RecordViewLastLine; ++i ) {
  5865.                 y2 += RecordViewCalcNextLine( i, FntLineHeight() );
  5866.                 if( i == RecordViewFirstPlainLine )
  5867.                     FntSetFont( AddrRecordFont );
  5868.                 if( y2 > ( bottomOfRecordViewDisplay - FntLineHeight() ))
  5869.                     break;
  5870.                 if( RecordViewLines[ i ].offset == 0 ) {
  5871.                     switch( RecordViewLines[ i ].fieldNum ) {
  5872.                         case phone1:
  5873.                         case phone2:
  5874.                         case phone3:
  5875.                         case phone4:
  5876.                         case phone5:
  5877.                             if( RecordViewLines[ i ].offset == 0 ) {
  5878.                                 if( x >= RecordViewLines[ i ].x
  5879.                                     && y >= y2
  5880.                                     && y <= (y2 + RecordViewCalcNextLine(i+1, FntLineHeight()))) {
  5881.                                         DialHumanPhoneNumber( &recordViewRecord.fields[
  5882.                                                                 RecordViewLines[i].fieldNum][
  5883.                                                                 RecordViewLines[i].offset],
  5884.                                                                 RecordViewLines[i].length );
  5885.                                         
  5886.                                         /*dialSize =  RecordViewLines[i].length;
  5887.                                         if( dialSize > 15 )
  5888.                                             dialSize = 15;
  5889.                                         StrNCopy(    dial,
  5890.                                                     &recordViewRecord.fields[
  5891.                                                         RecordViewLines[i].fieldNum][
  5892.                                                         RecordViewLines[i].offset],
  5893.                                                     dialSize );
  5894.                                         dial[ 15 ] = 0;
  5895.                                         FrmCustomAlert( DialAreaCodeAlert, dial, NULL, NULL );*/
  5896.                                         dialed = true;
  5897.                                 }
  5898.                             }
  5899.                             break;
  5900.                         default:
  5901.                             break;
  5902.                     }
  5903.                 }
  5904.             }
  5905.             FntSetFont( curFont );
  5906.         }
  5907.         
  5908.         if( !dialed )
  5909.             FrmGotoForm( EditView );
  5910.         
  5911.         handled = true;
  5912.     }
  5913.     
  5914.     return( handled );
  5915.     
  5916.     
  5917.     /*AddrAppInfoPtr appInfoPtr;
  5918.    UInt y;
  5919.    UInt i;
  5920.    FontID curFont;
  5921.    FormPtr   frm;
  5922.    UInt phoneLabelNum;
  5923.    CharPtr fieldLabelString;
  5924.    UInt fieldLabelLength;
  5925.    Word upIndex;
  5926.    Word downIndex;
  5927.    Boolean scrollableUp;
  5928.    Boolean scrollableDown;
  5929.    RectangleType r;
  5930.    int bottomOfRecordViewDisplay;
  5931.  
  5932.  
  5933.    appInfoPtr = (AddrAppInfoPtr) AddrAppInfoGetPtr(AddrDB);
  5934.                
  5935.             
  5936.    MemPtrUnlock(appInfoPtr);
  5937.    FntSetFont (curFont);
  5938.    
  5939.    
  5940.    // Now show/hide the scroll arrows
  5941.    frm = FrmGetActiveForm ();
  5942.  
  5943.    scrollableUp = TopRecordViewLine != 0;
  5944.    scrollableDown = i < RecordViewLastLine; 
  5945.  
  5946.  
  5947.    // Update the scroll button.
  5948.    upIndex = FrmGetObjectIndex (frm, RecordUpButton);
  5949.    downIndex = FrmGetObjectIndex (frm, RecordDownButton);
  5950.    FrmUpdateScrollers (frm, upIndex, downIndex, scrollableUp, scrollableDown);*/
  5951.     
  5952.     
  5953.    /*________________________________________ 
  5954.    Boolean      handled = false;
  5955.    FormPtr       frm;
  5956.    RectangleType r;
  5957.    short         x, y;
  5958.    Boolean      penDown;
  5959.    
  5960.    
  5961.    // If the user taps in the RecordViewDisplay take them to the Edit View
  5962.    frm = FrmGetActiveForm();
  5963.    FrmGetObjectBounds(frm, FrmGetObjectIndex(frm, RecordViewDisplay), &r);
  5964.    if (RctPtInRectangle (event->screenX, event->screenY, &r))
  5965.       {
  5966.       do 
  5967.          {
  5968.          PenGetPoint (&x, &y, &penDown);
  5969.          } while (penDown);
  5970.       
  5971.       if (RctPtInRectangle (x, y, &r))
  5972.          FrmGotoForm (EditView);
  5973.          
  5974.       handled = true;
  5975.       }
  5976.       
  5977.    return handled;*/
  5978. }
  5979.  
  5980.  
  5981. /***********************************************************************
  5982.  *
  5983.  * FUNCTION:    RecordViewDoCommand
  5984.  *
  5985.  * DESCRIPTION: This routine performs the menu command specified.
  5986.  *
  5987.  * PARAMETERS:  command  - menu item id
  5988.  *
  5989.  * RETURNED:    nothing
  5990.  *
  5991.  * REVISION HISTORY:
  5992.  *         Name   Date      Description
  5993.  *         ----   ----      -----------
  5994.  *         roger   6/27/95   Initial Revision
  5995.  *
  5996.  ***********************************************************************/
  5997. static Boolean RecordViewDoCommand (Word command)
  5998. {   
  5999.    switch (command)
  6000.        {
  6001.       case RecordRecordDeleteRecordCmd:
  6002.          if (DetailsDeleteRecord ())
  6003.              {
  6004.             recordViewRecordH = 0;      // freed by the last routine
  6005.             FrmGotoForm (ListView);
  6006.              }
  6007.          return true;
  6008.          
  6009.       case RecordRecordAttachNoteCmd:
  6010.          if (CreateNote())
  6011.             FrmGotoForm (NoteView);
  6012.          // CreateNote may or may not have freed the record.  Compare
  6013.          // the record's handle to recordViewRecordH.  If they differ
  6014.          // the record is new and recordViewRecordH shouldn't be freed
  6015.          // by the frmClose.
  6016.          if (recordViewRecordH != DmQueryRecord(AddrDB, CurrentRecord))
  6017.             recordViewRecordH = 0;
  6018.          return true;
  6019.          
  6020.       case RecordRecordDeleteNoteCmd:
  6021.          if (recordViewRecord.fields[note] != NULL &&
  6022.             FrmAlert(DeleteNoteAlert) == DeleteNoteYes)
  6023.             {
  6024.             DeleteNote ();
  6025.             // Deleting the note caused the record to be unlocked
  6026.             // Get it again for the record view's usage
  6027.             AddrGetRecord (AddrDB, CurrentRecord, &recordViewRecord, 
  6028.                &recordViewRecordH);
  6029.             
  6030.             RecordViewUpdate ();
  6031.              }
  6032.          return true;
  6033.          
  6034.       case RecordRecordSelectBusinessCardCmd:
  6035.          MenuEraseStatus (0);
  6036.          if (FrmAlert(SelectBusinessCardAlert) == SelectBusinessCardYes)
  6037.              {
  6038.                 DmRecordInfo (AddrDB, CurrentRecord, NULL, &BusinessCardRecordID, NULL);
  6039.              RecordViewDrawBusinessCardIndicator (FrmGetActiveForm());
  6040.              }
  6041.          return true;
  6042.       
  6043.       case RecordRecordSendBusinessCardCmd:
  6044.          MenuEraseStatus (0);
  6045.           AddrSendBusinessCard(AddrDB);
  6046.          return true;
  6047.       
  6048.       case RecordRecordSendRecordCmd:
  6049.         MenuEraseStatus (0);
  6050.         AddrSendRecord(AddrDB, CurrentRecord);
  6051.         return true;
  6052.       
  6053.       /*
  6054.       case RecordRecordSendCategoryCmd:
  6055.          MenuEraseStatus (0);
  6056.           AddrSendCategory(AddrDB, CurrentCategory);
  6057.          return true;
  6058.       */
  6059.       
  6060.         case RecordOptionsFontCmd:
  6061.          MenuEraseStatus (0);
  6062.             AddrRecordFont = SelectFont (AddrRecordFont);
  6063.             return true;
  6064.  
  6065.       case RecordOptionsEditCustomFldsCmd:
  6066.          MenuEraseStatus (0);
  6067.          FrmPopupForm (CustomEditDialog);
  6068.          return true;
  6069.                   
  6070.       case RecordOptionsAboutCmd:
  6071.          MenuEraseStatus (0);
  6072.          AbtShowAbout (sysFileCAddress);
  6073.          return true;
  6074.         
  6075.        }
  6076.       
  6077.    return false;
  6078. }
  6079.  
  6080.  
  6081. /***********************************************************************
  6082.  *
  6083.  * FUNCTION:    RecordViewHandleEvent
  6084.  *
  6085.  * DESCRIPTION: This routine is the event handler for the "Address View"
  6086.  *              of the Address Book application.
  6087.  *
  6088.  * PARAMETERS:  event  - a pointer to an EventType structure
  6089.  *
  6090.  * RETURNED:    true if the event has handle and should not be passed
  6091.  *              to a higher level handler.
  6092.  *
  6093.  * REVISION HISTORY:
  6094.  *         Name   Date      Description
  6095.  *         ----   ----      -----------
  6096.  *         art   6/5/95   Initial Revision
  6097.  *
  6098.  ***********************************************************************/
  6099. static Boolean RecordViewHandleEvent (EventPtr event)
  6100. {
  6101.    FormPtr frm;
  6102.    Boolean handled = false;
  6103.  
  6104.  
  6105.    switch (event->eType)
  6106.         {
  6107.       case ctlSelectEvent:
  6108.          switch (event->data.ctlSelect.controlID)
  6109.              {
  6110.             case RecordDoneButton:
  6111.                // When we return to the ListView highlight this record.
  6112.                ListViewSelectThisRecord = CurrentRecord;
  6113.                FrmGotoForm (ListView);
  6114.                handled = true;
  6115.                break;
  6116.  
  6117.             case RecordEditButton:
  6118.                FrmGotoForm (EditView);
  6119.                handled = true;
  6120.                break;
  6121.  
  6122.             case RecordNewButton:
  6123.                EditViewNewRecord();
  6124.                handled = true;
  6125.                break;
  6126.             default:
  6127.                break;
  6128.              }
  6129.          break;
  6130.    
  6131.       
  6132.       case penDownEvent:
  6133.          handled = RecordViewHandlePen(event);
  6134.          break;
  6135.       
  6136.       
  6137.       case ctlRepeatEvent:
  6138.          switch (event->data.ctlRepeat.controlID)
  6139.              {
  6140.             case RecordUpButton:
  6141.                RecordViewScroll(up);
  6142.                // leave unhandled so the buttons can repeat
  6143.                break;
  6144.  
  6145.             case RecordDownButton:
  6146.                RecordViewScroll(down);
  6147.                // leave unhandled so the buttons can repeat
  6148.                break;
  6149.             default:
  6150.  
  6151.                break;
  6152.              }
  6153.          break;
  6154.  
  6155.  
  6156.       case keyDownEvent:
  6157.          if (TxtCharIsHardKey(event->data.keyDown.modifiers, event->data.keyDown.chr))
  6158.              {
  6159.             FrmGotoForm (ListView);
  6160.             handled = true;
  6161.              } 
  6162.          else switch (event->data.keyDown.chr)
  6163.              {
  6164.             case pageUpChr:
  6165.                RecordViewScroll (up);
  6166.                handled = true;
  6167.                break;
  6168.                
  6169.             case pageDownChr:
  6170.                RecordViewScroll (down);
  6171.                handled = true;
  6172.                break;
  6173.                
  6174.             case sendDataChr:
  6175.                AddrSendRecord(AddrDB, CurrentRecord);
  6176.                handled = true;
  6177.                break;
  6178.             
  6179.             default:
  6180.                break;
  6181.              }
  6182.          break;
  6183.  
  6184.       
  6185.       case menuEvent:
  6186.          return RecordViewDoCommand (event->data.menu.itemID);
  6187.  
  6188.       
  6189.       case frmUpdateEvent:
  6190.             if (event->data.frmUpdate.updateCode & 
  6191.                     (updateCustomFieldLabelChanged | updateFontChanged))
  6192.                 {
  6193.             RecordViewUpdate ();
  6194.              }
  6195.          handled = true;
  6196.          break;
  6197.  
  6198.  
  6199.       case frmCloseEvent:
  6200.          if (recordViewRecordH)
  6201.              {
  6202.             MemHandleUnlock(recordViewRecordH);
  6203.             recordViewRecordH = 0;
  6204.              }
  6205.          MemHandleFree(MemPtrRecoverHandle(RecordViewLines));
  6206.          RecordViewLines = 0;
  6207.          break;
  6208.       
  6209.       case frmOpenEvent:
  6210.          frm = FrmGetActiveForm ();
  6211.          RecordViewInit (frm);
  6212.          FrmDrawForm (frm);
  6213.          RecordViewDraw(0, 0, 0);
  6214.          RecordViewDrawBusinessCardIndicator (frm);
  6215.          PriorAddressFormID = FrmGetFormId (frm);
  6216.  
  6217.          handled = true;
  6218.          break;
  6219.  
  6220.       case frmGotoEvent:
  6221.          frm = FrmGetActiveForm ();
  6222.          CurrentRecord = event->data.frmGoto.recordNum;
  6223.          RecordViewInit (frm);
  6224.          RecordViewMakeVisible(event->data.frmGoto.matchFieldNum, 
  6225.             event->data.frmGoto.matchPos, event->data.frmGoto.matchLen);
  6226.          FrmDrawForm (frm);
  6227.          RecordViewDraw(event->data.frmGoto.matchFieldNum, 
  6228.             event->data.frmGoto.matchPos, event->data.frmGoto.matchLen);
  6229.          PriorAddressFormID = FrmGetFormId (frm);
  6230.          handled = true;
  6231.          break;
  6232.         
  6233.         default:
  6234.             break;
  6235.        }
  6236.  
  6237.    return (handled);
  6238. }
  6239.  
  6240.  
  6241. #pragma mark ----------------
  6242. /***********************************************************************
  6243.  *
  6244.  * FUNCTION:    DetermineRecordName
  6245.  *
  6246.  * DESCRIPTION: Determines an address book record's name.  The name
  6247.  * varies based on which fields exist and what the sort order is.
  6248.  *
  6249.  * PARAMETERS:  name1, name2 - first and seconds names to draw
  6250.  *              name1Length, name2Length - length of the names in chars
  6251.  *              name1Width, name2Width - width of the names when drawn
  6252.  *              nameExtent - the space the names must be drawn in
  6253.  *              *x, y - where the names are drawn
  6254.  *              shortenedFieldWidth - the width in the current font
  6255.  *              
  6256.  *
  6257.  * RETURNED:    x is set after the last char drawn
  6258.  *                     Boolean - name1/name2 priority based on sortByCompany
  6259.  *
  6260.  * REVISION HISTORY:
  6261.  *            Name        Date        Description
  6262.  *            ----        ----        -----------
  6263.  *            roger        6/20/95    Initial Revision
  6264.  *            frigino    970813    Added priority return value
  6265.  *
  6266.  ***********************************************************************/
  6267.  
  6268. extern Boolean DetermineRecordName (AddrDBRecordPtr recordP, 
  6269.    UInt *shortenedFieldWidth, UInt *fieldSeparatorWidth, Boolean sortByCompany,
  6270.    CharPtr *name1, Int *name1Length, Int *name1Width, 
  6271.    CharPtr *name2, Int *name2Length, Int *name2Width,
  6272.    CharPtr *unnamedRecordStringPtr, UInt nameExtent)
  6273. {
  6274.    UInt fieldNameChoiceList[4];
  6275.    UInt fieldNameChoice;
  6276.    Boolean ignored;
  6277.     Boolean name1HasPriority;
  6278.  
  6279.    *shortenedFieldWidth = (FntCharWidth('.') * shortenedFieldLength);
  6280.    *fieldSeparatorWidth = FntCharsWidth (fieldSeparatorString, 
  6281.       fieldSeparatorLength);
  6282.  
  6283.  
  6284.    *name1 = NULL;
  6285.    *name2 = NULL;
  6286.          
  6287.    if (sortByCompany)
  6288.       {
  6289.         // When sorting by company, always treat name2 as priority.
  6290.         name1HasPriority = false;
  6291.  
  6292.       fieldNameChoiceList[3] = addressFieldsCount;
  6293.       fieldNameChoiceList[2] = firstName;
  6294.       fieldNameChoiceList[1] = name;
  6295.       fieldNameChoiceList[0] = company;
  6296.       fieldNameChoice = 0;
  6297.       
  6298.       while (*name1 == NULL && 
  6299.          fieldNameChoiceList[fieldNameChoice] != addressFieldsCount)
  6300.          {
  6301.          *name1 = recordP->fields[fieldNameChoiceList[fieldNameChoice++]];
  6302.          }   
  6303.             
  6304.         // When sorting by company, treat name2 as priority if we
  6305.         // succeed in getting the company name as the name1
  6306.         // Did we get the company name?
  6307.         if (fieldNameChoice > 1) {
  6308.             // No. We got a last name, first name, or nothing. Priority switches to name1
  6309.             name1HasPriority = true;
  6310.         }
  6311.  
  6312.       while (*name2 == NULL && 
  6313.          fieldNameChoiceList[fieldNameChoice] != addressFieldsCount)
  6314.          {
  6315.          *name2 = recordP->fields[fieldNameChoiceList[fieldNameChoice++]];
  6316.          }   
  6317.       
  6318.       }
  6319.    else
  6320.       {
  6321.         // When not sorting by company, always treat name1 as priority.
  6322.         name1HasPriority = true;
  6323.  
  6324.       fieldNameChoiceList[3] = addressFieldsCount;
  6325.       fieldNameChoiceList[2] = addressFieldsCount;
  6326.       fieldNameChoiceList[1] = firstName;
  6327.       fieldNameChoiceList[0] = name;
  6328.       fieldNameChoice = 0;
  6329.       
  6330.       while (*name1 == NULL && 
  6331.          fieldNameChoiceList[fieldNameChoice] != addressFieldsCount)
  6332.          {
  6333.          *name1 = recordP->fields[fieldNameChoiceList[fieldNameChoice++]];
  6334.          }   
  6335.       
  6336.       if (*name1 == NULL)
  6337.          {
  6338.          *name1 = recordP->fields[company];
  6339.          *name2 = NULL;
  6340.          }
  6341.       else
  6342.          {
  6343.          while (*name2 == NULL && 
  6344.             fieldNameChoiceList[fieldNameChoice] != addressFieldsCount)
  6345.             {
  6346.             *name2 = recordP->fields[fieldNameChoiceList[fieldNameChoice++]];
  6347.             }
  6348.          }         
  6349.       }
  6350.  
  6351.    if (*name1)
  6352.       {
  6353.       // Only show text from the first line in the field
  6354.       *name1Length = nameExtent;            // longer than possible
  6355.       *name1Width = nameExtent;            // wider than possible
  6356.       FntCharsInWidth (*name1, name1Width, name1Length, &ignored); //lint !e64
  6357.       }
  6358.    else
  6359.       {
  6360.       // Set the name to the unnamed string
  6361.       if (*unnamedRecordStringPtr == NULL)
  6362.          {
  6363.          *unnamedRecordStringPtr = MemHandleLock(DmGetResource(strRsc, UnnamedRecordStr));
  6364.          }
  6365.       
  6366.       // The unnamed string is assumed to be well chosen to not need clipping.
  6367.       *name1 = *unnamedRecordStringPtr;
  6368.       *name1Length = StrLen(*unnamedRecordStringPtr);
  6369.       *name1Width = FntCharsWidth (*unnamedRecordStringPtr, *name1Length);
  6370.       }
  6371.       
  6372.    if (*name2)
  6373.       {
  6374.       // Only show text from the first line in the field
  6375.       *name2Length = nameExtent;            // longer than possible
  6376.       *name2Width = nameExtent;            // wider than possible
  6377.       FntCharsInWidth (*name2, name2Width, name2Length, &ignored);//lint !e64
  6378.       }
  6379.    else
  6380.       { 
  6381.       *name2Length = 0;
  6382.       *name2Width = 0;
  6383.       }
  6384.  
  6385.     // Return priority status
  6386.     return name1HasPriority;
  6387. }
  6388.  
  6389.  
  6390. /***********************************************************************
  6391.  *
  6392.  * FUNCTION:    DrawRecordName
  6393.  *
  6394.  * DESCRIPTION: Draws an address book record name.  It is used
  6395.  * for the list view and note view.
  6396.  *
  6397.  * PARAMETERS:  name1, name2 - first and seconds names to draw
  6398.  *              name1Length, name2Length - length of the names in chars
  6399.  *              name1Width, name2Width - width of the names when drawn
  6400.  *              nameExtent - the space the names must be drawn in
  6401.  *              *x, y - where the names are drawn
  6402.  *              shortenedFieldWidth - the width in the current font
  6403.  *              
  6404.  *
  6405.  * RETURNED:    x is set after the last char drawn
  6406.  *
  6407.  * REVISION HISTORY:
  6408.  *            Name        Date        Description
  6409.  *            ----        ----        -----------
  6410.  *            roger        6/20/95    Initial Revision
  6411.  *            frigino    970813    Rewritten. Now includes a variable ratio for
  6412.  *                                    name1/name2 width allocation, a prioritization
  6413.  *                                    parameter, and a word break search to allow
  6414.  *                                    reclaiming of space from the low priority name.
  6415.  *
  6416.  ***********************************************************************/
  6417.  
  6418. extern void DrawRecordName (
  6419.     CharPtr name1, Int name1Length, Int name1Width, 
  6420.     CharPtr name2, Int name2Length, Int name2Width,
  6421.     UInt nameExtent, SWord *x, SWord y, UInt shortenedFieldWidth, 
  6422.     UInt fieldSeparatorWidth, Boolean center, Boolean priorityIsName1)
  6423. {
  6424.     Int        name1MaxWidth;
  6425.     Int        name2MaxWidth;
  6426.     Boolean    ignored;
  6427.     Int    totalWidth;
  6428. //    CharPtr    highPriName;
  6429.     CharPtr    lowPriName;
  6430.     Int    highPriNameWidth;
  6431.     Int    lowPriNameWidth;
  6432.     
  6433.     
  6434.     // Check if both names fit
  6435.     totalWidth = name1Width + (name2 ? fieldSeparatorWidth : 0) + name2Width;
  6436.     
  6437.     // If we are supposed to center the names then move in the x position
  6438.     // by the amount that centers the text
  6439.     if (center && (nameExtent > totalWidth))
  6440.         {
  6441.         *x += (nameExtent - totalWidth) / 2;
  6442.         }
  6443.  
  6444.     // Special case if only name1 is given
  6445.     if (name2 == NULL)
  6446.         {
  6447.         // Draw name portion that fits in extent
  6448.         FntCharsInWidth(name1, (Int*)&nameExtent, &name1Length, &ignored);
  6449.         WinDrawChars(name1, name1Length, *x, y);
  6450.         // Add width of characters actually drawn
  6451.         *x += FntCharsWidth(name1, name1Length);
  6452.         return;
  6453.         }
  6454.  
  6455.     // Remove name separator width
  6456.     nameExtent -= fieldSeparatorWidth;
  6457.     
  6458.     // Test if both names fit 
  6459.     if ((name1Width + name2Width) <= nameExtent)
  6460.         {
  6461.         name1MaxWidth = name1Width;
  6462.         name2MaxWidth = name2Width;
  6463.         }
  6464.     else
  6465.         {
  6466.         // They dont fit. One or both needs truncation
  6467.         // Establish name priorities and their allowed widths
  6468.         // Change this to alter the ratio of the low and high priority name spaces
  6469.         Int    highPriMaxWidth = (nameExtent << 1) / 3;    // 1/3 to low and 2/3 to high
  6470.         Int    lowPriMaxWidth = nameExtent - highPriMaxWidth;
  6471.         
  6472.         // Save working copies of names and widths based on priority
  6473.         if (priorityIsName1)
  6474.             {
  6475.             // Priority is name1
  6476. //            highPriName = name1;
  6477.             highPriNameWidth = name1Width;
  6478.             lowPriName = name2;
  6479.             lowPriNameWidth = name2Width;
  6480.             }
  6481.         else
  6482.             {
  6483.             // Priority is name2
  6484. //            highPriName = name2;
  6485.             highPriNameWidth = name2Width;
  6486.             lowPriName = name1;
  6487.             lowPriNameWidth = name1Width;
  6488.             }
  6489.  
  6490.         // Does high priority name fit in high priority max width?
  6491.         if (highPriNameWidth > highPriMaxWidth)
  6492.             {
  6493.             // No. Look for word break in low priority name
  6494.             CharPtr spaceP = StrChr(lowPriName, spaceChr);
  6495.             if (spaceP != NULL)
  6496.                 {
  6497.                 // Found break. Set low priority name width to break width
  6498.                 lowPriNameWidth = FntCharsWidth(lowPriName, spaceP - lowPriName);
  6499.                 // Reclaim width from low pri name width to low pri max width, if smaller
  6500.                 if (lowPriNameWidth < lowPriMaxWidth)
  6501.                     {
  6502.                     lowPriMaxWidth = lowPriNameWidth;
  6503.                     // Set new high pri max width
  6504.                     highPriMaxWidth = nameExtent - lowPriMaxWidth;
  6505.                     }
  6506.                 }
  6507.             }
  6508.         else
  6509.             {
  6510.             // Yes. Adjust maximum widths
  6511.             highPriMaxWidth = highPriNameWidth;
  6512.             lowPriMaxWidth = nameExtent - highPriMaxWidth;
  6513.             }
  6514.         
  6515.         // Convert priority widths back to name widths
  6516.         if (priorityIsName1)
  6517.             {
  6518.             // Priority is name1
  6519.             name1Width = highPriNameWidth;
  6520.             name2Width = lowPriNameWidth;
  6521.             name1MaxWidth = highPriMaxWidth;
  6522.             name2MaxWidth = lowPriMaxWidth;
  6523.             }
  6524.         else
  6525.             {
  6526.             // Priority is name2
  6527.             name1Width = lowPriNameWidth;
  6528.             name2Width = highPriNameWidth;
  6529.             name1MaxWidth = lowPriMaxWidth;
  6530.             name2MaxWidth = highPriMaxWidth;
  6531.             }
  6532.         }
  6533.  
  6534.     // Does name1 fit in its maximum width?
  6535.     if (name1Width > name1MaxWidth)
  6536.         {
  6537.         // No. Draw it to max width minus the ellipsis
  6538.         name1Width = name1MaxWidth - shortenedFieldWidth;
  6539.         FntCharsInWidth(name1, &name1Width, &name1Length, &ignored);
  6540.         WinDrawChars(name1, name1Length, *x, y);
  6541.         *x += name1Width;
  6542.         
  6543.         // Draw ellipsis
  6544.         WinDrawChars(shortenedFieldString, shortenedFieldLength, *x, y);
  6545.         *x += shortenedFieldWidth;
  6546.         }
  6547.     else
  6548.         {
  6549.         // Yes. Draw name1 within its width
  6550.         FntCharsInWidth(name1, &name1Width, &name1Length, &ignored);
  6551.         WinDrawChars(name1, name1Length, *x, y);
  6552.         *x += name1Width;
  6553.         }
  6554.     
  6555.     // Draw name separator
  6556.     WinDrawChars(fieldSeparatorString, fieldSeparatorLength, *x, y);
  6557.     *x += fieldSeparatorWidth;
  6558.     
  6559.     // Draw name2 within its maximum width
  6560.     FntCharsInWidth(name2, &name2MaxWidth, &name2Length, &ignored);
  6561.     WinDrawChars(name2, name2Length, *x, y);
  6562.     *x += name2MaxWidth;
  6563. }
  6564.  
  6565.  
  6566. /***********************************************************************
  6567.  *
  6568.  * FUNCTION:    DrawRecordNameAndPhoneNumber
  6569.  *
  6570.  * DESCRIPTION: Draws the namd and phone number (plus which phone)
  6571.  * within the screen bounds passed.
  6572.  *
  6573.  * PARAMETERS:  record - record to draw
  6574.  *              bounds - bounds of the draw region
  6575.  *              phoneLabelLetters - the first letter of each phone label
  6576.  *              sortByCompany - true if the database is sorted by company
  6577.  *              unnamedRecordStringPtr - string to use for unnamed records
  6578.  *
  6579.  * RETURNED:    nothing
  6580.  *
  6581.  * REVISION HISTORY:
  6582.  *         Name   Date      Description
  6583.  *         ----   ----      -----------
  6584.  *         roger   6/21/95   Initial Revision
  6585.  *
  6586.  ***********************************************************************/
  6587.  
  6588. static void DrawRecordNameAndPhoneNumber (AddrDBRecordPtr record, 
  6589.    RectanglePtr bounds, CharPtr phoneLabelLetters, Boolean sortByCompany,
  6590.    CharPtr *unnamedRecordStringPtr)
  6591. {
  6592.    SWord x, y;
  6593.    UInt phoneLabel;
  6594.    UInt fieldSeparatorWidth;
  6595.    UInt shortenedFieldWidth;
  6596.    CharPtr name1, name2, phone;
  6597.    Int name1Length;
  6598.    Int name2Length;
  6599.    Int phoneLength;
  6600.    Int name1Width;
  6601.    Int name2Width;
  6602.    Int phoneWidth;
  6603.    UInt nameExtent;
  6604.    Boolean ignored;
  6605.     Boolean name1HasPriority;
  6606.     Byte phoneLabelWidth;
  6607.    
  6608.  
  6609.    x = bounds->topLeft.x;
  6610.    y = bounds->topLeft.y;
  6611.    
  6612.    phoneLabelWidth = FntCharWidth('W') - 1;        // remove the blank trailing column
  6613.  
  6614.    bounds->extent.x -= (phoneLabelWidth + 1);
  6615.    
  6616.    name1HasPriority = DetermineRecordName(record, &shortenedFieldWidth,
  6617.        &fieldSeparatorWidth, sortByCompany, &name1, &name1Length, &name1Width, 
  6618.       &name2, &name2Length, &name2Width, unnamedRecordStringPtr, bounds->extent.x);
  6619.  
  6620.       
  6621.    phone = record->fields[phone1 + record->options.phones.displayPhoneForList];
  6622.    if (phone)
  6623.       {
  6624.       // Only show text from the first line in the field
  6625.       phoneWidth = bounds->extent.x;
  6626.       phoneLength = phoneWidth;         // more characters than we can expect
  6627.       FntCharsInWidth (phone, &phoneWidth, &phoneLength, &ignored);
  6628.       }
  6629.    else
  6630.       {
  6631.       phoneLength = 0;
  6632.       phoneWidth = 0;
  6633.       }
  6634.  
  6635.       
  6636.    if (bounds->extent.x   >= name1Width + (name2 ? fieldSeparatorWidth : 0) +
  6637.       name2Width + (phone ? spaceBetweenNamesAndPhoneNumbers : 0) + phoneWidth)
  6638.       {
  6639.       // we can draw it all!
  6640.       WinDrawChars(name1, name1Length, x, y);
  6641.       x += name1Width;
  6642.             
  6643.       // Is there a second name?
  6644.       if (name2)
  6645.          {
  6646.          if (name1)
  6647.             {
  6648.             WinDrawChars(fieldSeparatorString, fieldSeparatorLength, x, y);
  6649.             x += fieldSeparatorWidth;
  6650.             }
  6651.             
  6652.          // draw name2
  6653.          WinDrawChars(name2, name2Length, x, y);
  6654.          x += name2Width;
  6655.          }
  6656.       
  6657.       if (phone)
  6658.          WinDrawChars(phone, phoneLength, bounds->topLeft.x + bounds->extent.x 
  6659.             - phoneWidth, y);
  6660.                   
  6661.       }
  6662.    else
  6663.       {
  6664.         // Shortened math (970812 maf)
  6665.         nameExtent = bounds->extent.x - min(phoneWidth, PhoneColumnWidth);
  6666.  
  6667.       // Leave some space between names and numbers if there is a phone number      
  6668.       if (phone)
  6669.          nameExtent -= spaceBetweenNamesAndPhoneNumbers;
  6670.  
  6671.       DrawRecordName (name1, name1Length, name1Width, name2, name2Length, name2Width,
  6672.          nameExtent, &x, y, shortenedFieldWidth, fieldSeparatorWidth, false,
  6673.          name1HasPriority || !sortByCompany);
  6674.       
  6675.       if (phone)
  6676.          {
  6677.          x += spaceBetweenNamesAndPhoneNumbers;
  6678.          nameExtent = x - bounds->topLeft.x;
  6679.          
  6680.          
  6681.          // Now draw the phone number
  6682.          if (bounds->extent.x - nameExtent >= phoneWidth)
  6683.             {
  6684.             // We can draw it all
  6685.             WinDrawChars(phone, phoneLength, bounds->topLeft.x + bounds->extent.x 
  6686.                - phoneWidth, y);
  6687.             }
  6688.          else
  6689.             {
  6690.             // The phone number should be right justified instead of using
  6691.             // x from above because the string printed may be shorter
  6692.             // than we expect (CharsInWidth chops off space chars).
  6693.             phoneWidth = bounds->extent.x - nameExtent - shortenedFieldWidth;
  6694.             FntCharsInWidth(phone, &phoneWidth, &phoneLength, &ignored);
  6695.             WinDrawChars(phone, phoneLength, bounds->topLeft.x + bounds->extent.x -
  6696.                shortenedFieldWidth - phoneWidth, y);
  6697.                
  6698.             WinDrawChars(shortenedFieldString, shortenedFieldLength, 
  6699.                bounds->topLeft.x + bounds->extent.x - shortenedFieldWidth, y);
  6700.             }
  6701.          }
  6702.       }
  6703.    
  6704.    
  6705.    if (phone)
  6706.       {
  6707.       // Draw the first letter of the phone field label
  6708.       phoneLabel = GetPhoneLabel(record, firstPhoneField + 
  6709.          record->options.phones.displayPhoneForList);
  6710.       WinDrawChars (&phoneLabelLetters[phoneLabel], 1,
  6711.          bounds->topLeft.x + bounds->extent.x + 1 + ((phoneLabelWidth - 
  6712.          (FntCharWidth(phoneLabelLetters[phoneLabel]) - 1)) >> 1), y);//lint !e702
  6713.       }
  6714. }
  6715.  
  6716.  
  6717. /***********************************************************************
  6718.  *
  6719.  * FUNCTION:    ListViewLookupString
  6720.  *
  6721.  * DESCRIPTION: Adds a character to ListLookupField, looks up the 
  6722.  * string in the database and selects the item that matches.
  6723.  *
  6724.  * PARAMETERS:  event - EventPtr containing character to add to ListLookupField
  6725.  *                      
  6726.  * RETURNED:    true if the field handled the event
  6727.  *
  6728.  * REVISION HISTORY:
  6729.  *         Name   Date      Description
  6730.  *         ----   ----      -----------
  6731.  *         roger   6/15/95   Initial Revision
  6732.  *
  6733.  ***********************************************************************/
  6734. static Boolean ListViewLookupString (EventPtr event)
  6735. {
  6736.    FormPtr frm;
  6737.    UInt fldIndex;
  6738.    FieldPtr fldP;
  6739.    CharPtr fldTextP;
  6740.    TablePtr tableP;
  6741.    UInt foundRecord;
  6742.    Boolean completeMatch;
  6743.    Int length;
  6744.    
  6745.             
  6746.    frm = FrmGetActiveForm();
  6747.    fldIndex = FrmGetObjectIndex(frm, ListLookupField);
  6748.    FrmSetFocus(frm, fldIndex);
  6749.    fldP = FrmGetObjectPtr (frm, fldIndex);
  6750.  
  6751.    
  6752.    if (FldHandleEvent (fldP, event) || event->eType == fldChangedEvent)
  6753.       {
  6754.       fldTextP = FldGetTextPtr(fldP);
  6755.       tableP = FrmGetObjectPtr (frm, FrmGetObjectIndex(frm, ListTable));
  6756.  
  6757.       if (!AddrLookupString(AddrDB, fldTextP, SortByCompany, 
  6758.          CurrentCategory, &foundRecord, &completeMatch))
  6759.          {
  6760.          // If the user deleted the lookup text remove the
  6761.          // highlight.
  6762.          TblUnhighlightSelection(tableP);
  6763.          }
  6764.       else
  6765.          {
  6766.          ListViewSelectRecord(foundRecord);
  6767.          }
  6768.       
  6769.       
  6770.       if (!completeMatch)
  6771.          {
  6772.          // Delete the last character added.
  6773.          length = FldGetTextLength(fldP);
  6774.          FldDelete(fldP, length - 1, length);
  6775.          
  6776.          SndPlaySystemSound (sndError);
  6777.          }
  6778.          
  6779.       return true;
  6780.       }
  6781.  
  6782.    // Event not handled
  6783.    return false;
  6784.    
  6785. }
  6786.  
  6787.  
  6788. /***********************************************************************
  6789.  *
  6790.  * FUNCTION:    ListViewDrawRecord
  6791.  *
  6792.  * DESCRIPTION: This routine draws an address book record.  It is called as
  6793.  *              a callback routine by the table object.
  6794.  *
  6795.  * PARAMETERS:  table  - pointer to the address list table
  6796.  *              row    - row number, in the table, of the item to draw
  6797.  *              column - column number, in the table, of the item to draw
  6798.  *              bounds - bounds of the draw region
  6799.  *
  6800.  * RETURNED:    nothing
  6801.  *
  6802.  * REVISION HISTORY:
  6803.  *         Name   Date      Description
  6804.  *         ----   ----      -----------
  6805.  *         roger   6/21/95   Initial Revision
  6806.  *
  6807.  ***********************************************************************/
  6808.  
  6809. static void ListViewDrawRecord (VoidPtr table, Word row, Word column, 
  6810.    RectanglePtr bounds)
  6811. {
  6812.    Word recordNum;
  6813.    Err error;
  6814.    AddrDBRecordType record;
  6815.    Handle recordH;
  6816.    char noteChar;
  6817.    FontID currFont;
  6818.    
  6819.  
  6820.  
  6821.    // Get the record number that corresponds to the table item to draw.
  6822.    // The record number is stored in the "intValue" field of the item.
  6823.    // 
  6824.    recordNum = TblGetRowID (table, row);
  6825.  
  6826.    error = AddrGetRecord (AddrDB, recordNum, &record, &recordH);
  6827.    ErrNonFatalDisplayIf ((error), "Record not found");
  6828.    if (error) return;
  6829.  
  6830.  
  6831.    if (column == 0)
  6832.       {
  6833.       currFont = FntSetFont (AddrListFont);
  6834.       DrawRecordNameAndPhoneNumber (&record, bounds, PhoneLabelLetters, 
  6835.          SortByCompany, &UnnamedRecordStringPtr);
  6836.         FntSetFont (currFont);
  6837.       }
  6838.    else
  6839.       {
  6840.         // Draw a not symbol if the field has a note
  6841.       if (record.fields[note])
  6842.           {
  6843.          currFont = FntSetFont (symbolFont);
  6844.          noteChar = symbolNote;
  6845.          WinDrawChars (¬eChar, 1, bounds->topLeft.x, bounds->topLeft.y);
  6846.             FntSetFont (currFont);
  6847.             }
  6848.       }
  6849.    MemHandleUnlock(recordH);
  6850. }
  6851.  
  6852.  
  6853. /***********************************************************************
  6854.  *
  6855.  * FUNCTION:    ListClearLookupString
  6856.  *
  6857.  * DESCRIPTION: Clears the ListLookupField.  Does not unhighlight the item.
  6858.  *
  6859.  * PARAMETERS:  nothing
  6860.  *                      
  6861.  * RETURNED:    nothing
  6862.  *
  6863.  * REVISION HISTORY:
  6864.  *         Name   Date      Description
  6865.  *         ----   ----      -----------
  6866.  *         roger   6/16/95   Initial Revision
  6867.  *
  6868.  ***********************************************************************/
  6869. static void ListClearLookupString ()
  6870. {
  6871.    FormPtr frm;
  6872.    UInt fldIndex;
  6873.    FieldPtr fldP;
  6874.    Int length;
  6875.  
  6876.  
  6877.    frm = FrmGetActiveForm();
  6878.    FrmSetFocus(frm, noFocus);
  6879.    fldIndex = FrmGetObjectIndex(frm, ListLookupField);
  6880.    fldP = FrmGetObjectPtr (frm, fldIndex);
  6881.  
  6882.    length = FldGetTextLength(fldP);
  6883.    if (length > 0)
  6884.       FldDelete(fldP, 0, length);
  6885. }
  6886.  
  6887.  
  6888. /***********************************************************************
  6889.  *
  6890.  * FUNCTION:    ListViewNumberOfRows
  6891.  *
  6892.  * DESCRIPTION: This routine return the maximun number of visible rows,
  6893.  *              with the current list view font setting.
  6894.  *
  6895.  * PARAMETERS:  table - List View table
  6896.  *
  6897.  * RETURNED:    maximun number of displayable rows
  6898.  *
  6899.  * REVISION HISTORY:
  6900.  *            Name    Date        Description
  6901.  *            ----    ----        -----------
  6902.  *            art    8/28/97    Initial Revision
  6903.  *
  6904.  ***********************************************************************/
  6905. static Word ListViewNumberOfRows (TablePtr table)
  6906. {
  6907.     Word                rows;
  6908.     Word                rowsInTable;
  6909.     Word                tableHeight;
  6910.     FontID            currFont;
  6911.     RectangleType    r;
  6912.  
  6913.  
  6914.     rowsInTable = TblGetNumberOfRows (table);
  6915.  
  6916.     TblGetBounds (table, &r);
  6917.     tableHeight = r.extent.y;
  6918.  
  6919.     currFont = FntSetFont (AddrListFont);
  6920.     rows = tableHeight / FntLineHeight ();
  6921.     FntSetFont (currFont);
  6922.  
  6923.     if (rows <= rowsInTable)
  6924.         return (rows);
  6925.     else
  6926.         return (rowsInTable);
  6927. }
  6928.  
  6929.  
  6930. /***********************************************************************
  6931.  *
  6932.  * FUNCTION:    ListViewUpdateScrollButtons
  6933.  *
  6934.  * DESCRIPTION: Show or hide the list view scroll buttons.
  6935.  *
  6936.  * PARAMETERS:  nothing
  6937.  *
  6938.  * RETURNED:    nothing
  6939.  *
  6940.  * REVISION HISTORY:
  6941.  *         Name   Date      Description
  6942.  *         ----   ----      -----------
  6943.  *         roger   6/21/95   Initial Revision
  6944.  *
  6945.  ***********************************************************************/
  6946. static void ListViewUpdateScrollButtons (void)
  6947. {
  6948.    SWord      row;
  6949.    Word upIndex;
  6950.    Word downIndex;
  6951.    Word recordNum;
  6952.    Boolean scrollableUp;
  6953.    Boolean scrollableDown;
  6954.    FormPtr   frm;
  6955.    TablePtr table;
  6956.  
  6957.    frm = FrmGetActiveForm ();
  6958.  
  6959.    // Update the button that scroll the list.
  6960.    //
  6961.    // If the first record displayed is not the fist record in the category,
  6962.    // enable the up scroller.
  6963.    recordNum = TopVisibleRecord;
  6964.    scrollableUp = SeekRecord (&recordNum, 1, dmSeekBackward);
  6965.  
  6966.  
  6967.    // Find the record in the last row of the table
  6968.    table = GetObjectPtr (ListTable);
  6969.     row = TblGetLastUsableRow (table);
  6970.     if (row != -1)
  6971.         recordNum = TblGetRowID (table, row);
  6972.  
  6973.  
  6974.    // If the last record displayed is not the last record in the category,
  6975.    // enable the down scroller.
  6976.    scrollableDown = SeekRecord (&recordNum, 1, dmSeekForward);
  6977.  
  6978.  
  6979.    // Update the scroll button.
  6980.    upIndex = FrmGetObjectIndex (frm, ListUpButton);
  6981.    downIndex = FrmGetObjectIndex (frm, ListDownButton);
  6982.    FrmUpdateScrollers (frm, upIndex, downIndex, scrollableUp, scrollableDown);
  6983.  
  6984. }
  6985.  
  6986.  
  6987. /***********************************************************************
  6988.  *
  6989.  * FUNCTION:    ListLoadTable
  6990.  *
  6991.  * DESCRIPTION: This routine loads address book database records into
  6992.  *              the list view form.
  6993.  *
  6994.  * PARAMETERS:  nothing
  6995.  *
  6996.  * RETURNED:    nothing
  6997.  *
  6998.  * REVISION HISTORY:
  6999.  *         Name   Date      Description
  7000.  *         ----   ----      -----------
  7001.  *         art   6/5/95      Initial Revision
  7002.  *
  7003.  ***********************************************************************/
  7004. static void ListLoadTable (void)
  7005. {
  7006.    Word      row;
  7007.    Word      numRows;
  7008.     Word        lineHeight;
  7009.     Word        recordNum;
  7010.     Word        visibleRows;
  7011.     FontID    currFont;
  7012.    TablePtr table;
  7013.  
  7014.    
  7015.    // For each row in the table, store the record number as the row id.
  7016.    table = GetObjectPtr (ListTable);
  7017.  
  7018.  
  7019.    TblUnhighlightSelection(table);
  7020.  
  7021.    // Make sure we haven't scrolled too far down the list of records
  7022.    // leaving blank lines in the table.
  7023.  
  7024.    // Try going forward to the last record that should be visible
  7025.     visibleRows = ListViewNumberOfRows (table);
  7026.     recordNum = TopVisibleRecord;
  7027.     if (!SeekRecord (&recordNum, visibleRows - 1, dmSeekForward))
  7028.       {
  7029.       // We have at least one line without a record.  Fix it.
  7030.       // Try going backwards one page from the last record
  7031.       TopVisibleRecord = dmMaxRecordIndex;
  7032.         if (!SeekRecord (&TopVisibleRecord, visibleRows - 1, dmSeekBackward))
  7033.          {
  7034.          // Not enough records to fill one page.  Start with the first record
  7035.          TopVisibleRecord = 0;
  7036.          SeekRecord (&TopVisibleRecord, 0, dmSeekForward);
  7037.          }
  7038.       }
  7039.  
  7040.  
  7041.     currFont = FntSetFont (AddrListFont);
  7042.     lineHeight = FntLineHeight ();
  7043.     FntSetFont (currFont);
  7044.  
  7045.    numRows = TblGetNumberOfRows (table);
  7046.    recordNum = TopVisibleRecord;
  7047.  
  7048.     for (row = 0; row < visibleRows; row++)
  7049.       {
  7050.       if ( ! SeekRecord (&recordNum, 0, dmSeekForward))
  7051.          break;
  7052.  
  7053.       // Make the row usable.
  7054.       TblSetRowUsable (table, row, true);
  7055.  
  7056.       // Mark the row invalid so that it will draw when we call the 
  7057.       // draw routine.
  7058.       TblMarkRowInvalid (table, row);
  7059.  
  7060.       // Store the record number as the row id.
  7061.       TblSetRowID (table, row, recordNum);
  7062.  
  7063.         TblSetItemFont (table, row, nameAndNumColumn, AddrListFont);
  7064.         TblSetRowHeight (table, row, lineHeight);
  7065.  
  7066.       recordNum++;
  7067.       }
  7068.    
  7069.  
  7070.    // Hide the item that don't have any data.
  7071.    while (row < numRows)
  7072.       {      
  7073.       TblSetRowUsable (table, row, false);
  7074.       row++;
  7075.       }
  7076.  
  7077.    ListViewUpdateScrollButtons();
  7078. }
  7079.  
  7080.  
  7081. /***********************************************************************
  7082.  *
  7083.  * FUNCTION:    ListViewSelectCategory
  7084.  *
  7085.  * DESCRIPTION: This routine handles selection, creation and deletion of
  7086.  *              categories form the Details Dialog.  
  7087.  *
  7088.  * PARAMETERS:  nothing
  7089.  *
  7090.  * RETURNED:    The index of the new category.
  7091.  *
  7092.  *              The following global variables are modified:
  7093.  *                     CurrentCategory
  7094.  *                     ShowAllCategories
  7095.  *                     CategoryName
  7096.  *
  7097.  * REVISION HISTORY:
  7098.  *         Name   Date      Description
  7099.  *         ----   ----      -----------
  7100.  *         art   6/5/95      Initial Revision
  7101.  *
  7102.  ***********************************************************************/
  7103. static Word ListViewSelectCategory (void)
  7104. {
  7105.    FormPtr frm;
  7106.    TablePtr table;
  7107.    Word category;
  7108.    Boolean categoryEdited;
  7109.    
  7110.    // Process the category popup list.  
  7111.    category = CurrentCategory;
  7112.  
  7113.    frm = FrmGetActiveForm();
  7114.    categoryEdited = CategorySelect (AddrDB, frm, ListCategoryTrigger,
  7115.                  ListCategoryList, true, &category, CategoryName, 1, 0);
  7116.    
  7117.    if (category == dmAllCategories)
  7118.       ShowAllCategories = true;
  7119.    else
  7120.       ShowAllCategories = false;
  7121.       
  7122.    if ( categoryEdited || (category != CurrentCategory))
  7123.       {
  7124.       ChangeCategory (category);
  7125.  
  7126.       // Display the new category.
  7127.       ListLoadTable ();
  7128.       table = GetObjectPtr (ListTable);
  7129.       TblEraseTable (table);
  7130.       TblDrawTable (table);
  7131.       
  7132.       ListClearLookupString ();
  7133.        
  7134.        // By changing the category the current record is lost.
  7135.        CurrentRecord = noRecord;
  7136.       }
  7137.  
  7138.    return (category);
  7139. }
  7140.  
  7141.  
  7142. /***********************************************************************
  7143.  *
  7144.  * FUNCTION:    ListViewNextCategory
  7145.  *
  7146.  * DESCRIPTION: This routine display the next category,  if the last
  7147.  *              catagory is being displayed  
  7148.  *
  7149.  * PARAMETERS:  nothing
  7150.  *
  7151.  * RETURNED:    nothing
  7152.  *
  7153.  *              The following global variables are modified:
  7154.  *                     CurrentCategory
  7155.  *                     ShowAllCategories
  7156.  *                     CategoryName
  7157.  *
  7158.  * REVISION HISTORY:
  7159.  *         Name   Date      Description
  7160.  *         ----   ----      -----------
  7161.  *         art   9/15/95   Initial Revision
  7162.  *         rsf   9/20/95   Copied from To Do
  7163.  *
  7164.  ***********************************************************************/
  7165. static void ListViewNextCategory (void)
  7166. {
  7167.    Word category;
  7168.    TablePtr table;
  7169.    ControlPtr ctl;   
  7170.  
  7171.  
  7172.    category = CategoryGetNext (AddrDB, CurrentCategory);
  7173.     
  7174.     if (category != CurrentCategory)
  7175.         {
  7176.        if (category == dmAllCategories)
  7177.           ShowAllCategories = true;
  7178.        else
  7179.           ShowAllCategories = false;
  7180.  
  7181.        ChangeCategory (category);
  7182.  
  7183.        // Set the label of the category trigger.
  7184.        ctl = GetObjectPtr (ListCategoryTrigger);
  7185.        CategoryGetName (AddrDB, CurrentCategory, CategoryName);
  7186.        CategorySetTriggerLabel (ctl, CategoryName);
  7187.  
  7188.  
  7189.        // Display the new category.
  7190.        ListLoadTable ();
  7191.        table = GetObjectPtr (ListTable);
  7192.        TblEraseTable (table);
  7193.        TblDrawTable (table);
  7194.        
  7195.        // By changing the category the current record is lost.
  7196.        CurrentRecord = noRecord;
  7197.        }
  7198. }
  7199.  
  7200.  
  7201. /***********************************************************************
  7202.  *
  7203.  * FUNCTION:    ListViewScroll
  7204.  *
  7205.  * DESCRIPTION: This routine scrolls the list of names and phone numbers 
  7206.  *              in the direction specified.
  7207.  *
  7208.  * PARAMETERS:  direction    - up or dowm
  7209.  *              units        - unit amount to scroll
  7210.  *              byLine        - if true, list scrolls in line units
  7211.  *                                    - if false, list scrolls in page units
  7212.  *
  7213.  * RETURNED:    nothing
  7214.  *
  7215.  * REVISION HISTORY:
  7216.  *            Name        Date        Description
  7217.  *            ----        ----        -----------
  7218.  *            art        6/5/95    Initial Revision
  7219.  *       frigino    8/14/97    Modified to scroll by line or page in units
  7220.  *
  7221.  ***********************************************************************/
  7222. static void ListViewScroll (DirectionType direction, UInt units, Boolean byLine)
  7223. {
  7224.     TablePtr table;
  7225.     Word rowsInPage;
  7226.     UInt newTopVisibleRecord;
  7227.     
  7228.     table = GetObjectPtr (ListTable);
  7229.     // Safe. There must be at least one row in the table.
  7230.     rowsInPage = ListViewNumberOfRows (table) - 1;
  7231.     newTopVisibleRecord = TopVisibleRecord;
  7232.  
  7233.     // Scroll the table down.
  7234.     if (direction == down)
  7235.         {
  7236.         // Scroll down by line units
  7237.         if (byLine)
  7238.             {
  7239.             // Scroll down by the requested number of lines
  7240.             if (!SeekRecord (&newTopVisibleRecord, units, dmSeekForward))
  7241.                 {
  7242.                 // Tried to scroll past bottom. Goto last record
  7243.                 newTopVisibleRecord = dmMaxRecordIndex;
  7244.                 SeekRecord (&newTopVisibleRecord, 1, dmSeekBackward);
  7245.                 }
  7246.             }
  7247.         // Scroll in page units
  7248.         else
  7249.             {
  7250.             // Try scrolling down by the requested number of pages
  7251.             if (!SeekRecord (&newTopVisibleRecord, units * rowsInPage, dmSeekForward))
  7252.                 {
  7253.                 // Hit bottom. Try going backwards one page from the last record
  7254.                 newTopVisibleRecord = dmMaxRecordIndex;
  7255.                 if (!SeekRecord (&newTopVisibleRecord, rowsInPage, dmSeekBackward))
  7256.                     {
  7257.                     // Not enough records to fill one page. Goto the first record
  7258.                     newTopVisibleRecord = 0;
  7259.                     SeekRecord (&newTopVisibleRecord, 0, dmSeekForward);
  7260.                     }
  7261.                 }
  7262.             }
  7263.         }
  7264.     // Scroll the table up
  7265.     else
  7266.         {
  7267.         // Scroll up by line units
  7268.         if (byLine)
  7269.             {
  7270.             // Scroll up by the requested number of lines
  7271.             if (!SeekRecord (&newTopVisibleRecord, units, dmSeekBackward))
  7272.                 {
  7273.                 // Tried to scroll past top. Goto first record
  7274.                 newTopVisibleRecord = 0;
  7275.                 SeekRecord (&newTopVisibleRecord, 0, dmSeekForward);
  7276.                 }
  7277.             }
  7278.         // Scroll in page units
  7279.         else
  7280.             {
  7281.             // Try scrolling up by the requested number of pages
  7282.             if (!SeekRecord (&newTopVisibleRecord, units * rowsInPage, dmSeekBackward))
  7283.                 {
  7284.                 // Hit top. Goto the first record
  7285.                 newTopVisibleRecord = 0;
  7286.                 SeekRecord (&newTopVisibleRecord, 0, dmSeekForward);
  7287.                 }
  7288.             }
  7289.         }
  7290.  
  7291.  
  7292.     // Avoid redraw if no change
  7293.     if (TopVisibleRecord != newTopVisibleRecord)
  7294.         {
  7295.         TopVisibleRecord = newTopVisibleRecord;
  7296.         ListLoadTable();
  7297.         TblRedrawTable(table);
  7298.         }
  7299. }
  7300.  
  7301.  
  7302. /***********************************************************************
  7303.  *
  7304.  * FUNCTION:    ListViewSelectRecord
  7305.  *
  7306.  * DESCRIPTION: Selects (highlights) a record on the table, scrolling
  7307.  *              the record if neccessary.  Also sets the CurrentRecord.
  7308.  *
  7309.  * PARAMETERS:  recordNum - record to select
  7310.  *                      
  7311.  * RETURNED:    nothing
  7312.  *
  7313.  * REVISION HISTORY:
  7314.  *         Name   Date      Description
  7315.  *         ----   ----      -----------
  7316.  *         roger   6/30/95   Initial Revision
  7317.  *
  7318.  ***********************************************************************/
  7319. static void ListViewSelectRecord (UInt recordNum)
  7320. {
  7321.    UInt row, column;
  7322.    TablePtr tableP;
  7323.    UInt attr;
  7324.  
  7325.  
  7326.    ErrFatalDisplayIf (recordNum >= DmNumRecords(AddrDB), "Record outside AddrDB");
  7327.  
  7328.  
  7329.    tableP = GetObjectPtr (ListTable);
  7330.  
  7331.  
  7332.    // Don't change anything if the same record is selected
  7333.    if (TblGetSelection(tableP, &row, &column) &&
  7334.       recordNum == TblGetRowID (tableP, row))
  7335.       {
  7336.       return;
  7337.       }
  7338.       
  7339.  
  7340.    // See if the record is displayed by one of the rows in the table
  7341.    // A while is used because if TblFindRowID fails we need to
  7342.    // call it again to find the row in the reloaded table.
  7343.    while (!TblFindRowID(tableP, recordNum, &row))
  7344.       {
  7345.       if (HideSecretRecords)
  7346.          {
  7347.          // If the record is hidden stop trying to show it.
  7348.          DmRecordInfo(AddrDB, recordNum, &attr, NULL, NULL);
  7349.          if (attr & dmRecAttrSecret)
  7350.             {
  7351.             return;
  7352.             }
  7353.          }
  7354.             
  7355.       // Scroll the view down placing the item
  7356.       // on the top row
  7357.       TopVisibleRecord = recordNum;
  7358.  
  7359.       // Make sure that TopVisibleRecord is visible in CurrentCategory
  7360.       if (CurrentCategory != dmAllCategories)
  7361.          {
  7362.          // Get the category and the secret attribute of the current record.
  7363.          DmRecordInfo (AddrDB, TopVisibleRecord, &attr, NULL, NULL);   
  7364.          if ((attr & dmRecAttrCategoryMask) != CurrentCategory)
  7365.             {
  7366.             ErrNonFatalDisplay("Record not in CurrentCategory");
  7367.             CurrentCategory = (attr & dmRecAttrCategoryMask);
  7368.             }
  7369.          }
  7370.    
  7371.       ListLoadTable();
  7372.       TblRedrawTable(tableP);
  7373.       }
  7374.  
  7375.    
  7376.    // Select the item
  7377.    TblSelectItem (tableP, row, nameAndNumColumn);
  7378.    
  7379.    CurrentRecord = recordNum;
  7380. }   
  7381.    
  7382.  
  7383. /***********************************************************************
  7384.  *
  7385.  * FUNCTION:    ListViewUpdateDisplay
  7386.  *
  7387.  * DESCRIPTION: This routine update the display of the list view
  7388.  *
  7389.  * PARAMETERS:  updateCode - a code that indicated what changes have been
  7390.  *                           made to the to do list.
  7391.  *                      
  7392.  * RETURNED:    nothing
  7393.  *
  7394.  * REVISION HISTORY:
  7395.  *         Name   Date      Description
  7396.  *         ----   ----      -----------
  7397.  *         art   6/5/95      Initial Revision
  7398.  *
  7399.  ***********************************************************************/
  7400. static void ListViewUpdateDisplay (Word updateCode)
  7401. {
  7402.    TablePtr table;
  7403.    
  7404.    table = GetObjectPtr (ListTable);
  7405.  
  7406.  
  7407.    if (updateCode & updateRedrawAll)
  7408.       {
  7409.       ListLoadTable();
  7410.       TblRedrawTable (table);
  7411.       }
  7412.  
  7413.     if (updateCode & updateFontChanged)
  7414.         {
  7415.         ListLoadTable();
  7416.         TblRedrawTable (table);
  7417.         if (CurrentRecord != noRecord)
  7418.             ListViewSelectRecord (CurrentRecord);
  7419.         }
  7420.  
  7421.    if (updateCode & updateSelectCurrentRecord)
  7422.       {
  7423.       ListViewSelectRecord(CurrentRecord);
  7424.       }
  7425. }
  7426.  
  7427.  
  7428. /***********************************************************************
  7429.  *
  7430.  * FUNCTION:    ListViewDoCommand
  7431.  *
  7432.  * DESCRIPTION: This routine performs the menu command specified.
  7433.  *
  7434.  * PARAMETERS:  command  - menu item id
  7435.  *
  7436.  * RETURNED:    nothing
  7437.  *
  7438.  * REVISION HISTORY:
  7439.  *         Name   Date      Description
  7440.  *         ----   ----      -----------
  7441.  *         art   6/5/95      Initial Revision
  7442.  *
  7443.  ***********************************************************************/
  7444. static Boolean ListViewDoCommand (Word command)
  7445. {   
  7446.     switch (command)
  7447.       {
  7448.       /*
  7449.       case ListRecordDeleteRecordCmd:
  7450.             if (CurrentRecord != noRecord)
  7451.                 {
  7452.                 if (DetailsDeleteRecord ())
  7453.                     {
  7454.                     ListViewUpdateDisplay (updateRedrawAll);
  7455.                     }
  7456.                 }
  7457.             else
  7458.                 SndPlaySystemSound (sndError);
  7459.             return true;
  7460.       */
  7461.          
  7462.       /*
  7463.       case ListRecordSelectBusinessCardCmd:
  7464.             if (CurrentRecord != noRecord)
  7465.                 {
  7466.                  MenuEraseStatus (0);
  7467.                  if (FrmAlert(SelectBusinessCardAlert) == SelectBusinessCardYes)
  7468.                      {
  7469.                     DmRecordInfo (AddrDB, CurrentRecord, NULL, &BusinessCardRecordID, NULL);
  7470.                      }
  7471.                 }
  7472.             else
  7473.                 SndPlaySystemSound (sndError);
  7474.          return true;
  7475.       */
  7476.       
  7477.       case ListRecordSendBusinessCardCmd:
  7478.          MenuEraseStatus (0);
  7479.           AddrSendBusinessCard(AddrDB);
  7480.          return true;
  7481.  
  7482.       /*
  7483.       case ListRecordSendRecordCmd:
  7484.             if (CurrentRecord != noRecord)
  7485.                 {
  7486.                 MenuEraseStatus (0);
  7487.                 AddrSendRecord(AddrDB, CurrentRecord);
  7488.                 }
  7489.             else
  7490.                 SndPlaySystemSound (sndError);
  7491.             return true;
  7492.       */
  7493.       
  7494.       case ListRecordSendCategoryCmd:
  7495.          MenuEraseStatus (0);
  7496.           AddrSendCategory(AddrDB, CurrentCategory);
  7497.          return true;
  7498.       
  7499.         case ListOptionsFontCmd:
  7500.          MenuEraseStatus (0);
  7501.             AddrListFont = SelectFont (AddrListFont);
  7502.             ListViewUpdateDisplay (updateRedrawAll);
  7503.          return true;
  7504.  
  7505.       case ListOptionsListByCmd:
  7506.          MenuEraseStatus (0);
  7507.          ListClearLookupString();
  7508.          FrmPopupForm (PreferencesDialog);
  7509.          return true;
  7510.          
  7511.       case ListOptionsEditCustomFldsCmd:
  7512.          MenuEraseStatus (0);
  7513.          FrmPopupForm (CustomEditDialog);
  7514.          return true;
  7515.                   
  7516.       case ListOptionsAboutCmd:
  7517.          MenuEraseStatus (0);
  7518.          AbtShowAbout (sysFileCAddress);
  7519.          return true;
  7520.                   
  7521.       default:
  7522.             break;
  7523.       }
  7524.    return false;
  7525. }
  7526.  
  7527.  
  7528. /***********************************************************************
  7529.  *
  7530.  * FUNCTION:    ListViewInit
  7531.  *
  7532.  * DESCRIPTION: This routine initializes the "List View" of the 
  7533.  *              Address application.
  7534.  *
  7535.  * PARAMETERS:  event  - a pointer to an EventType structure
  7536.  *
  7537.  * RETURNED:    true if the event has handle and should not be passed
  7538.  *              to a higher level handler.
  7539.  *
  7540.  * REVISION HISTORY:
  7541.  *         Name   Date      Description
  7542.  *         ----   ----      -----------
  7543.  *         art   6/5/95      Initial Revision
  7544.  *
  7545.  ***********************************************************************/
  7546. static void ListViewInit (FormPtr frm)
  7547. {
  7548.    Word row;
  7549.    Word rowsInTable;
  7550.    TablePtr table;
  7551.    ControlPtr ctl;
  7552.  
  7553.    if (ShowAllCategories)
  7554.       CurrentCategory = dmAllCategories;
  7555.  
  7556.  
  7557.    // Initialize the address list table.
  7558.    table = FrmGetObjectPtr (frm, FrmGetObjectIndex (frm, ListTable));
  7559.    rowsInTable = TblGetNumberOfRows (table);
  7560.    for (row = 0; row < rowsInTable; row++)
  7561.       {      
  7562.       TblSetItemStyle (table, row, nameAndNumColumn, customTableItem);
  7563.       TblSetItemStyle (table, row, noteColumn, customTableItem);
  7564.       TblSetRowUsable (table, row, false);
  7565.       }
  7566.  
  7567.    TblSetColumnUsable (table, nameAndNumColumn, true);
  7568.    TblSetColumnUsable (table, noteColumn, true);
  7569.  
  7570.  
  7571.    // Set the callback routine that will draw the records.
  7572.    TblSetCustomDrawProcedure (table, nameAndNumColumn, ListViewDrawRecord);
  7573.    TblSetCustomDrawProcedure (table, noteColumn, ListViewDrawRecord);
  7574.  
  7575.  
  7576.    // Load records into the address list.
  7577.    ListLoadTable ();
  7578.  
  7579.  
  7580.    // Set the label of the category trigger.
  7581.    ctl = GetObjectPtr (ListCategoryTrigger);
  7582.    CategoryGetName (AddrDB, CurrentCategory, CategoryName);
  7583.    CategorySetTriggerLabel (ctl, CategoryName);
  7584.  
  7585.    
  7586. //   ListViewUpdateScrollButtons();
  7587.    
  7588.    // Turn on the cursor in the lookup field.
  7589. //   FrmSetFocus(frm, FrmGetObjectIndex (frm, ListLookupField));
  7590. }
  7591.  
  7592.  
  7593. /***********************************************************************
  7594.  *
  7595.  * FUNCTION:    ListViewHandleEvent
  7596.  *
  7597.  * DESCRIPTION: This routine is the event handler for the "List View"
  7598.  *              of the Address Book application.
  7599.  *
  7600.  * PARAMETERS:  event  - a pointer to an EventType structure
  7601.  *
  7602.  * RETURNED:    true if the event has handle and should not be passed
  7603.  *              to a higher level handler.
  7604.  *
  7605.  * REVISION HISTORY:
  7606.  *            Name        Date        Description
  7607.  *            ----        ----        -----------
  7608.  *            art        6/5/95   Initial Revision
  7609.  *            frigino    8/15/97    Added scroll rate acceleration using
  7610.  *                                    ResetScrollRate() and AdjustScrollRate()
  7611.  *
  7612.  ***********************************************************************/
  7613. static Boolean ListViewHandleEvent (EventPtr event)
  7614. {
  7615.    FormPtr frm;
  7616.    Boolean handled = false;
  7617.    TablePtr table;
  7618.    UInt row;
  7619.    UInt column;
  7620.     
  7621.    switch (event->eType)
  7622.        {
  7623.       case tblSelectEvent:
  7624.          // An item in the list of names and phone numbers was selected, go to
  7625.          // the record view.
  7626.          CurrentRecord = TblGetRowID (event->data.tblSelect.pTable, 
  7627.                                       event->data.tblSelect.row);
  7628.  
  7629.          // Set the global variable that determines which field is the top visible
  7630.          // field in the edit view.  Also done when New is pressed.
  7631.          TopVisibleFieldIndex = 0;
  7632.          EditRowIDWhichHadFocus = editFirstFieldIndex;
  7633.          EditFieldPosition = 0;
  7634.          
  7635.          if (event->data.tblSelect.column == nameAndNumColumn)
  7636.             FrmGotoForm (RecordView);
  7637.          else
  7638.             if (CreateNote())
  7639.                FrmGotoForm (NoteView);
  7640.          handled = true;
  7641.          break;
  7642.  
  7643.       case ctlSelectEvent:
  7644.          switch (event->data.ctlSelect.controlID) {
  7645.             case ListCategoryTrigger:
  7646. /*
  7647. // SCL: Commented out test code as per Art (by phone 1/16/98)
  7648.                 //   Test code for the lookup function.
  7649.                 {
  7650.                 CharPtr resultString;
  7651.                 AddrLookupParamsType params;
  7652.  
  7653.                 // parameters for the lookup
  7654.                 params.title = "Send Email To:";
  7655.                 params.pasteButtonText = "Add";
  7656.                 StrCopy (params.lookupString, "1");
  7657.                 params.field1 = addrLookupSortField;
  7658.                 params.field2 = addrLookupEmail;
  7659.                 params.field2Optional = false;
  7660.                 params.userShouldInteract = true;
  7661.                 params.formatStringP = "^email";
  7662.  
  7663.                 //    params.field1 = addrLookupSortField;
  7664.                 //    params.field1 = addrLookupState;
  7665.                 //    params.field2 = addrLookupEmail;
  7666.                 //    params.field2 = addrLookupSortField;
  7667.                 //    params.field2 = addrLookupState;
  7668.                 //    params.field2 = addrLookupListPhone;
  7669.                 //    params.field2 = addrLookupNoField;
  7670.                 //    params.userShouldInteract = true;
  7671.                 //params.formatStringP = "^first ^name, ^title, ^company";
  7672.  
  7673.                 Lookup(¶ms);
  7674.  
  7675.                 if (params.resultStringH)
  7676.                 {
  7677.                 resultString = MemHandleLock(params.resultStringH);
  7678.                 MemHandleUnlock(params.resultStringH);
  7679.                 }
  7680.  
  7681.                 break;
  7682.                 }
  7683.                 // End test code
  7684. */
  7685.  
  7686.                ListViewSelectCategory ();
  7687.                handled = true;
  7688.                break;
  7689.  
  7690.             case ListNewButton:
  7691.                EditViewNewRecord();
  7692.                handled = true;
  7693.                break;
  7694.          }
  7695.          break;
  7696.  
  7697.         case ctlEnterEvent:
  7698.             switch (event->data.ctlEnter.controlID)
  7699.                 {
  7700.                 case ListUpButton:
  7701.                 case ListDownButton:
  7702.                     // Reset scroll rate
  7703.                     ResetScrollRate();
  7704.                     // Clear lookup string
  7705.                     ListClearLookupString ();
  7706.                     // leave unhandled so the buttons can repeat
  7707.                     break;
  7708.                 }
  7709.          break;
  7710.  
  7711.       case ctlRepeatEvent:
  7712.             // Adjust the scroll rate if necessary
  7713.             AdjustScrollRate();
  7714.  
  7715.          switch (event->data.ctlRepeat.controlID)
  7716.              {
  7717.             case ListUpButton:
  7718.                     ListViewScroll (up, ScrollUnits, false);
  7719.                     // leave unhandled so the buttons can repeat
  7720.                break;
  7721.                
  7722.             case ListDownButton:
  7723.                     ListViewScroll (down, ScrollUnits, false);
  7724.                     // leave unhandled so the buttons can repeat
  7725.                break;
  7726.             default:
  7727.                break;
  7728.              }
  7729.          break;
  7730.  
  7731.  
  7732.       case keyDownEvent:
  7733.          // Address Book key pressed for the first time?
  7734.          if (TxtCharIsHardKey(event->data.keyDown.modifiers, event->data.keyDown.chr))
  7735.              {
  7736.             if (! (event->data.keyDown.modifiers & poweredOnKeyMask))
  7737.                 {
  7738.                ListClearLookupString ();
  7739.                ListViewNextCategory ();
  7740.                handled = true;
  7741.                 }
  7742.             }
  7743.             else 
  7744.                 switch (event->data.keyDown.chr)
  7745.                     {
  7746.                    case pageUpChr:
  7747.                        // Reset scroll rate if not auto repeating
  7748.                        if ((event->data.keyDown.modifiers & autoRepeatKeyMask) == 0)
  7749.                            {
  7750.                            ResetScrollRate();
  7751.                            }
  7752.                         // Adjust the scroll rate if necessary
  7753.                         AdjustScrollRate();
  7754.                       ListViewScroll (up, ScrollUnits, false);
  7755.                       ListClearLookupString ();
  7756.                       handled = true;
  7757.                       break;
  7758.                       
  7759.                    case pageDownChr:
  7760.                        // Reset scroll rate if not auto repeating
  7761.                        if ((event->data.keyDown.modifiers & autoRepeatKeyMask) == 0)
  7762.                            {
  7763.                            ResetScrollRate();
  7764.                            }
  7765.                         // Adjust the scroll rate if necessary
  7766.                         AdjustScrollRate();
  7767.                       ListViewScroll (down, ScrollUnits, false);
  7768.                       ListClearLookupString ();
  7769.                       handled = true;
  7770.                       break;
  7771.  
  7772.                    case linefeedChr:
  7773.                       frm = FrmGetActiveForm ();
  7774.                       table = FrmGetObjectPtr (frm, FrmGetObjectIndex (frm, ListTable));
  7775.                       if (TblGetSelection (table, &row, &column))
  7776.                          {
  7777.                          // Set the global variable that determines which field is the top visible
  7778.                          // field in the edit view.  Also done when New is pressed.
  7779.                          TopVisibleFieldIndex = 0;
  7780.                       
  7781.                          FrmGotoForm (RecordView);
  7782.                          }
  7783.                       handled = true;
  7784.                       break;
  7785.  
  7786.                     case sendDataChr:
  7787.                         if (CurrentRecord != noRecord)
  7788.                             {
  7789.                             MenuEraseStatus (0);
  7790.                             AddrSendRecord(AddrDB, CurrentRecord);
  7791.                             }
  7792.                         else
  7793.                             SndPlaySystemSound (sndError);
  7794.                        handled = true;
  7795.                        break;
  7796.                     
  7797.                    default:
  7798.                       handled = ListViewLookupString(event);
  7799.                       break;
  7800.                     }
  7801.          break;
  7802.  
  7803.       case fldChangedEvent:
  7804.          ListViewLookupString(event);
  7805.          handled = true;
  7806.          break;
  7807.       
  7808.       case menuEvent:
  7809.          return ListViewDoCommand (event->data.menu.itemID);
  7810.       
  7811.       case frmCloseEvent:
  7812.          if (UnnamedRecordStringPtr)
  7813.              {
  7814.             MemPtrUnlock(UnnamedRecordStringPtr);
  7815.             UnnamedRecordStringPtr = NULL;
  7816. //          DmReleaseResource(strRsc, UnnamedRecordStr)
  7817.              }
  7818.          break;
  7819.  
  7820.       case frmOpenEvent:
  7821.          frm = FrmGetActiveForm ();
  7822.          ListViewInit (frm);
  7823.  
  7824.  
  7825.          // Make sure the record to be selected is one of the table's rows or
  7826.          // else it reloads the table with the record at the top.  Nothing is
  7827.          // drawn by this because the table isn't visible.
  7828.          if (ListViewSelectThisRecord != noRecord)
  7829.              {
  7830.             ListViewSelectRecord(ListViewSelectThisRecord);
  7831.              }
  7832.  
  7833.  
  7834.          FrmDrawForm (frm);
  7835.  
  7836.          // Select the record.  This finds which row to select it and does it.
  7837.          if (ListViewSelectThisRecord != noRecord)
  7838.              {
  7839.             ListViewSelectRecord(ListViewSelectThisRecord);
  7840.             ListViewSelectThisRecord = noRecord;
  7841.              }
  7842.       
  7843.          // Set the focus in the lookup field so that the user can easily
  7844.          // bring up the keyboard.
  7845.          FrmSetFocus(frm, FrmGetObjectIndex(frm, ListLookupField));
  7846.       
  7847.          PriorAddressFormID = FrmGetFormId (frm);
  7848.          handled = true;
  7849.          break;
  7850.  
  7851.       case frmUpdateEvent:
  7852.          ListViewUpdateDisplay (event->data.frmUpdate.updateCode);
  7853.          handled = true;
  7854.          break;
  7855.       
  7856.       default:
  7857.           break;
  7858.        }
  7859.       
  7860.    return (handled);
  7861. }
  7862.  
  7863.  
  7864. /***********************************************************************
  7865.  *
  7866.  * FUNCTION:    GetObjectPtr
  7867.  *
  7868.  * DESCRIPTION: This routine returns a pointer to an object in the current
  7869.  *              form.
  7870.  *
  7871.  * PARAMETERS:  formId - id of the form to display
  7872.  *
  7873.  * RETURNED:    nothing
  7874.  *
  7875.  * REVISION HISTORY:
  7876.  *         Name   Date      Description
  7877.  *         ----   ----      -----------
  7878.  *         art   6/5/95      Initial Revision
  7879.  *
  7880.  ***********************************************************************/
  7881. #if EMULATION_LEVEL == EMULATION_NONE
  7882. extern VoidPtr GetObjectPtr (Word objectID)
  7883. {
  7884.    FormPtr frm;
  7885.    
  7886.    frm = FrmGetActiveForm ();
  7887.    return (FrmGetObjectPtr (frm, FrmGetObjectIndex (frm, objectID)));
  7888.  
  7889. }
  7890. #endif
  7891.  
  7892.  
  7893. /***********************************************************************
  7894.  *
  7895.  * FUNCTION:    AddrSendBusinessCard
  7896.  *
  7897.  * DESCRIPTION: Send the Business Card record or complain if none selected.
  7898.  *
  7899.  * PARAMETERS:  dbP - the database
  7900.  *
  7901.  * RETURNED:    true if the record is found and sent
  7902.  *
  7903.  * REVISION HISTORY:
  7904.  *         Name   Date      Description
  7905.  *         ----   ----      -----------
  7906.  *         roger  10/20/97  Initial Revision
  7907.  *
  7908.  ***********************************************************************/
  7909. static Boolean AddrSendBusinessCard (DmOpenRef dbP)
  7910. {
  7911.     Word recordNum;
  7912.     
  7913.     
  7914.     if (DmFindRecordByID (AddrDB, BusinessCardRecordID, &recordNum) == dmErrUniqueIDNotFound ||
  7915.         DmQueryRecord(dbP, recordNum) == 0)
  7916.         FrmAlert(SendBusinessCardAlert);
  7917.     else
  7918.         {
  7919.         AddrSendRecord(dbP, recordNum);
  7920.         return true;
  7921.         }
  7922.     
  7923.     return false;
  7924. }
  7925.  
  7926.  
  7927. /***********************************************************************
  7928.  *
  7929.  * FUNCTION:     InitPhoneLabelLetters
  7930.  *
  7931.  * DESCRIPTION:  Init the list of first letters of phone labels.  Used
  7932.  * in the list view and for find.
  7933.  *
  7934.  * PARAMETERS:   appInfoPtr - contains the field labels
  7935.  *                 phoneLabelLetters - array of characters (one for each phone label)
  7936.  *
  7937.  * RETURNED:     nothing
  7938.  *
  7939.  * REVISION HISTORY:
  7940.  *         Name   Date      Description
  7941.  *         ----   ----      -----------
  7942.  *         roger   7/24/95   Initial Revision
  7943.  *
  7944.  ***********************************************************************/
  7945. extern void InitPhoneLabelLetters(AddrAppInfoPtr appInfoPtr, CharPtr phoneLabelLetters)
  7946. {
  7947.    UInt i;
  7948.  
  7949.  
  7950.    // Get the first char of the phone field labels for the list view.
  7951.    for (i = 0; i < numPhoneLabels; i++){
  7952.       phoneLabelLetters[i] = appInfoPtr->fieldLabels[i + 
  7953.          ((i < numPhoneLabelsStoredFirst) ? firstPhoneField : 
  7954.           (addressFieldsCount - numPhoneLabelsStoredFirst))][0];
  7955.    }
  7956. }
  7957.  
  7958.  
  7959. /***********************************************************************
  7960.  *
  7961.  * FUNCTION:    AppHandleKeyDown
  7962.  *
  7963.  * DESCRIPTION: Handle the key being down.
  7964.  *
  7965.  * PARAMETERS:  event  - a pointer to an EventType structure
  7966.  *
  7967.  * RETURNED:    true if the event was handled and should not be passed on
  7968.  *
  7969.  * REVISION HISTORY:
  7970.  *         Name   Date      Description
  7971.  *         ----   ----      -----------
  7972.  *         roger  10/22/97  Initial Revision
  7973.  *
  7974.  ***********************************************************************/
  7975. static Boolean AppHandleKeyDown (EventPtr event)
  7976. {
  7977.    // Check if a button being held down is released
  7978.    if (TickAppButtonPushed != 0)
  7979.        {
  7980.        // This is the case when the button is let up
  7981.        if ((KeyCurrentState() & (keyBitHard1 | keyBitHard2 | keyBitHard3 | keyBitHard4)) == 0)
  7982.            {
  7983.            if (BusinessCardSentForThisButtonPress)
  7984.                {
  7985.                BusinessCardSentForThisButtonPress = false;
  7986.               
  7987.               TickAppButtonPushed = 0;
  7988.               
  7989.                 // Allow the masked off key to now send keyDownEvents.
  7990.                 KeySetMask(keyBitsAll);
  7991.                }
  7992.            else if (event->eType == nilEvent)
  7993.                {
  7994.               // Send the keyDownEvent to the app.  It was stripped out
  7995.               // before but now it can be sent over the nullEvent.  It
  7996.               // may be nullChr from when the app was launched.  In that case
  7997.               // we don't need to send the app's key because the work expected,
  7998.               // which was switching to this app, has already been done.
  7999.               if (AppButtonPushed != nullChr)
  8000.                   {
  8001.                   event->eType = keyDownEvent;
  8002.                   event->data.keyDown.chr = AppButtonPushed;
  8003.                   event->data.keyDown.modifiers = AppButtonPushedModifiers;
  8004.                   }
  8005.               
  8006.               TickAppButtonPushed = 0;
  8007.               
  8008.                 // Allow the masked off key to now send keyDownEvents.
  8009.                 KeySetMask(keyBitsAll);
  8010.               }
  8011.            }
  8012.        // This is the case when the button is depresed long enough to send the business card
  8013.        else if (TickAppButtonPushed + AppButtonPushTimeout <= TimGetTicks() &&
  8014.            !BusinessCardSentForThisButtonPress)
  8015.            {
  8016.             BusinessCardSentForThisButtonPress = true;
  8017.             AddrSendBusinessCard(AddrDB);
  8018.             }
  8019.         }
  8020.        
  8021.    
  8022.     else if (event->eType == keyDownEvent)
  8023.         {
  8024.       if (TxtCharIsHardKey(event->data.keyDown.modifiers, event->data.keyDown.chr) &&
  8025.           !(event->data.keyDown.modifiers & autoRepeatKeyMask))
  8026.           {
  8027.             // Remember which hard key is mapped to the Address Book
  8028.             // because it may need to be sent later.
  8029.             AppButtonPushed = event->data.keyDown.chr;
  8030.             AppButtonPushedModifiers = event->data.keyDown.modifiers;
  8031.             
  8032.             TickAppButtonPushed = TimGetTicks();
  8033.             
  8034.             // Mask off the key to avoid repeat keys causing clicking sounds
  8035.             KeySetMask(~KeyCurrentState());
  8036.             
  8037.             // Don't process the key
  8038.             return true;
  8039.             }
  8040.         }
  8041.    
  8042.    return false;
  8043. }
  8044.  
  8045.  
  8046. /***********************************************************************
  8047.  *
  8048.  * FUNCTION:    ApplicationHandleEvent
  8049.  *
  8050.  * DESCRIPTION: This routine loads form resources and set the event
  8051.  *              handler for the form loaded.
  8052.  *
  8053.  * PARAMETERS:  event  - a pointer to an EventType structure
  8054.  *
  8055.  * RETURNED:    true if the event has handle and should not be passed
  8056.  *              to a higher level handler.
  8057.  *
  8058.  * REVISION HISTORY:
  8059.  *         Name   Date      Description
  8060.  *         ----   ----      -----------
  8061.  *         art   9/11/95      Initial Revision
  8062.  *
  8063.  ***********************************************************************/
  8064. static Boolean ApplicationHandleEvent (EventPtr event)
  8065. {
  8066.    Word formId;
  8067.    FormPtr frm;
  8068.  
  8069.    if (event->eType == frmLoadEvent)
  8070.        {
  8071.       
  8072.       // Load the form resource.
  8073.       formId = event->data.frmLoad.formID;
  8074.       frm = FrmInitForm (formId);
  8075.       FrmSetActiveForm (frm);      
  8076.       
  8077.       // Set the event handler for the form.  The handler of the currently
  8078.       // active form is called by FrmHandleEvent each time is receives an
  8079.       // event.
  8080.       switch (formId)
  8081.           {
  8082.          case ListView:
  8083.             FrmSetEventHandler(frm, ListViewHandleEvent);
  8084.             break;
  8085.       
  8086.          case RecordView:
  8087.             FrmSetEventHandler(frm, RecordViewHandleEvent);
  8088.             break;
  8089.             
  8090.          case EditView:
  8091.             FrmSetEventHandler(frm, EditViewHandleEvent);
  8092.             break;
  8093.             
  8094.          case NoteView:
  8095.             FrmSetEventHandler(frm, NoteViewHandleEvent);
  8096.             break;
  8097.             
  8098.          case DetailsDialog:
  8099.             FrmSetEventHandler(frm, DetailsHandleEvent);
  8100.             break;
  8101.             
  8102.          case CustomEditDialog:
  8103.             FrmSetEventHandler(frm, CustomEditHandleEvent);
  8104.             break;
  8105.             
  8106.          case PreferencesDialog:
  8107.             FrmSetEventHandler(frm, PreferencesDialogHandleEvent);
  8108.             break;
  8109.             
  8110.          default:
  8111.             ErrNonFatalDisplayIf(true, "Invalid Form Load Event");
  8112.             break;
  8113.             }
  8114.    
  8115.       return (true);
  8116.        }
  8117.    return (false);
  8118. }
  8119.  
  8120.  
  8121. /***********************************************************************
  8122.  *
  8123.  * FUNCTION:    EventLoop
  8124.  *
  8125.  * DESCRIPTION: This routine is the event loop for the Address Book
  8126.  *              aplication.  
  8127.  *
  8128.  * PARAMETERS:  nothing
  8129.  *
  8130.  * RETURNED:    nothing
  8131.  *
  8132.  * REVISION HISTORY:
  8133.  *         Name   Date      Description
  8134.  *         ----   ----      -----------
  8135.  *         roger   6/5/95   Initial Revision
  8136.  *
  8137.  ***********************************************************************/
  8138. static void EventLoop (void)
  8139. {
  8140.    Word error;
  8141.    EventType event;
  8142.  
  8143.    do
  8144.       {
  8145.       EvtGetEvent (&event, (TickAppButtonPushed == 0) ? evtWaitForever : 2);
  8146.       
  8147.       if (! SysHandleEvent (&event))
  8148.       
  8149.          if (! AppHandleKeyDown (&event))
  8150.          
  8151.              if (! MenuHandleEvent (0, &event, &error))
  8152.              
  8153.                 if (! ApplicationHandleEvent (&event))
  8154.        
  8155.                    FrmDispatchEvent (&event); 
  8156.       
  8157.       
  8158.       #if EMULATION_LEVEL != EMULATION_NONE
  8159. //         MemHeapCheck(0);         // Check the dynamic heap after every event
  8160. //         MemHeapCheck(1);         // Check the first heap after every event
  8161.       #endif
  8162.       }
  8163.    while (event.eType != appStopEvent);
  8164. }
  8165.  
  8166.  
  8167. /***********************************************************************
  8168.  *
  8169.  * FUNCTION:    PilotMain
  8170.  *
  8171.  * DESCRIPTION: This is the main entry point for the Address
  8172.  *              application.
  8173.  *
  8174.  * PARAMETERS:  nothing
  8175.  *
  8176.  * RETURNED:    nothing
  8177.  *
  8178.  * NOTE:        We need to create a branch island to PilotMain in order to 
  8179.  *              successfully link this application for the device.
  8180.  *
  8181.  * REVISION HISTORY:
  8182.  *         Name   Date      Description
  8183.  *         ----   ----      -----------
  8184.  *         art   7/24/95   Initial Revision
  8185.  *
  8186.  ***********************************************************************/
  8187. DWord   PilotMain (Word cmd, Ptr cmdPBP, Word launchFlags)
  8188. {
  8189.    return AddrPilotMain(cmd, cmdPBP, launchFlags);
  8190. }
  8191.  
  8192.  
  8193. /***********************************************************************
  8194.  *
  8195.  * FUNCTION:    AddressMain
  8196.  *
  8197.  * DESCRIPTION: This is the main entry point for the Address Book 
  8198.  *              application.
  8199.  *
  8200.  * PARAMETERS:  nothing
  8201.  *
  8202.  * RETURNED:    nothing
  8203.  *
  8204.  * REVISION HISTORY:
  8205.  *         Name   Date      Description
  8206.  *         ----   ----      -----------
  8207.  *         art   6/5/95      Initial Revision
  8208.  *
  8209.  ***********************************************************************/
  8210. // Note: We need to create a branch island to PilotMain in order to successfully
  8211. //  link this application for the device.
  8212. static DWord   AddrPilotMain (Word cmd, Ptr cmdPBP, Word launchFlags)
  8213. {
  8214.    Err error = 0;
  8215.  
  8216.     /* are we version compliant? */    
  8217.     error = RomVersionCompatible (0x03000000, launchFlags);
  8218.     if (error) return (error);
  8219.     
  8220.    switch (cmd)
  8221.        {
  8222.       case sysAppLaunchCmdNormalLaunch:
  8223.          error = StartApplication ();
  8224.          if (error) 
  8225.             return (error);
  8226.             
  8227.          FrmGotoForm (ListView);
  8228.          #if EMULATION_LEVEL != EMULATION_NONE
  8229. //            MemSetDebugMode(0x001A);   
  8230.          #endif
  8231.          EventLoop ();
  8232.          StopApplication ();
  8233.          break;
  8234.         
  8235.         
  8236.       case sysAppLaunchCmdFind:
  8237.          Search ((FindParamsPtr) cmdPBP);
  8238.          break;
  8239.         
  8240.         
  8241.       // This action code could be sent to the app when it's already running.
  8242.       case sysAppLaunchCmdGoTo:
  8243.          {
  8244.          Boolean launched;
  8245.          
  8246.          
  8247.          launched = launchFlags & sysAppLaunchFlagNewGlobals;
  8248.  
  8249.          if (launched) 
  8250.              {
  8251.             error = StartApplication ();
  8252.             if (error) 
  8253.                return (error);
  8254.              }
  8255.  
  8256.          GoToItem ((GoToParamsPtr)cmdPBP, launched);
  8257.  
  8258.          if (launched) 
  8259.              {
  8260.             EventLoop ();
  8261.             StopApplication ();   
  8262.              }      
  8263.          }
  8264.          break;
  8265.         
  8266.         
  8267.       case sysAppLaunchCmdSyncNotify:
  8268.          AppHandleSync();
  8269.          break;
  8270.         
  8271.         
  8272.       // Launch code sent to running app before sysAppLaunchCmdFind
  8273.       // or other action codes that will cause data searches or manipulation.
  8274.       case sysAppLaunchCmdSaveData:
  8275.          FrmSaveAllForms ();
  8276.          break;
  8277.         
  8278.         
  8279.       // We are requested to initialize an empty database (by sync app).
  8280.       case sysAppLaunchCmdInitDatabase:
  8281.          AppLaunchCmdDatabaseInit (((SysAppLaunchCmdInitDatabaseType*)cmdPBP)->dbP);
  8282.          break;
  8283.         
  8284.         
  8285.       // This launch code is sent after the system is reset. We use this time
  8286.       //  to create our default database if this is a hard reset
  8287.         // Note: This code is used by ROM based applications only. A RAM based 
  8288.         //  application will never receive this event.
  8289.       case sysAppLaunchCmdSystemReset:
  8290.          if (((SysAppLaunchCmdSystemResetType*)cmdPBP)->createDefaultDB) 
  8291.              {
  8292.             Handle resH;
  8293.             
  8294.             
  8295.             resH = DmGet1Resource(sysResTDefaultDB, sysResIDDefaultDB);
  8296.             if (resH) 
  8297.                 {
  8298.                DmCreateDatabaseFromImage(MemHandleLock(resH));
  8299.                MemHandleUnlock(resH);
  8300.                DmReleaseResource(resH);
  8301.                 }
  8302.                 // register to receive vcf files on hard reset
  8303.                 ExgRegisterData(sysFileCAddress, exgRegExtensionID, "vcf");
  8304.              }
  8305.          break;
  8306.         
  8307.         
  8308.       // Present the user with ui to perform a lookup and return a string
  8309.       // with information from the selected record.
  8310.       case sysAppLaunchCmdLookup:
  8311.          Lookup((AddrLookupParamsPtr) cmdPBP);
  8312.          break;
  8313.       
  8314.       
  8315.       // Present the user with ui to perform a lookup and return a string
  8316.       // with information from the selected record.
  8317.       case sysAppLaunchCmdExgReceiveData:
  8318.           {
  8319.           DmOpenRef dbP;
  8320.           
  8321.          // if our app is not active, we need to open the database 
  8322.          // the subcall flag is used here since this call can be made without launching the app
  8323.          if (!(launchFlags & sysAppLaunchFlagSubCall))
  8324.              {
  8325.              dbP = DmOpenDatabaseByTypeCreator (addrDBType, sysFileCAddress, dmModeReadWrite);
  8326.              }
  8327.          else
  8328.              dbP = AddrDB;
  8329.          
  8330.          if (dbP != NULL)
  8331.              {
  8332.                 error = AddrReceiveData(dbP, (ExgSocketPtr) cmdPBP);
  8333.                 
  8334.              if (!(launchFlags & sysAppLaunchFlagSubCall))
  8335.                     error = DmCloseDatabase(dbP);
  8336.                 }
  8337.             
  8338.             }
  8339.          break;
  8340.       
  8341.       
  8342.       // Present the user with ui to perform a lookup and return a string
  8343.       // with information from the selected record.
  8344. /*      case sysAppLaunchCmdSendData:
  8345.           if (((SendDataAppParamsPtr) ((SendDataParamsPtr) cmdPBP)->appDataP)->recordNum != noRecord &&
  8346.               ((SendDataParamsPtr) cmdPBP)->more == false)
  8347.               {
  8348.              AddrMakeRecord((SendDataParamsPtr) cmdPBP);
  8349.              }
  8350.           else
  8351.               {
  8352.              AddrMakeCategory((SendDataParamsPtr) cmdPBP);
  8353.              }
  8354.          break;
  8355.        
  8356.        
  8357. */
  8358.        }
  8359.    
  8360.    return error;
  8361. }
  8362.  
  8363. /***********************************************************************
  8364.  *
  8365.  * FUNCTION:    AddressLoadPrefs
  8366.  *
  8367.  * DESCRIPTION: Load the application preferences and fix them up if
  8368.  *                there's a version mismatch.
  8369.  *
  8370.  * PARAMETERS:  appInfoPtr    -- Pointer to the app info structure
  8371.  *
  8372.  * RETURNED:    nothing
  8373.  *
  8374.  * REVISION HISTORY:
  8375.  *         Name   Date      Description
  8376.  *         ----   ----      -----------
  8377.  *         BGT   1/8/98     Initial revision
  8378.  *
  8379.  ***********************************************************************/
  8380.  
  8381. void AddressLoadPrefs(AddrAppInfoPtr            appInfoPtr)
  8382. {
  8383.     SWord prefsVersion;
  8384.     Word prefsSize;
  8385.     AddrPreferenceType prefs;
  8386.  
  8387.    // Read the preferences / saved-state information.  There is only one
  8388.    // version of the Address Book preferences so don't worry about multiple
  8389.    // versions.  Users appreciate the transferal of their preferences from prior
  8390.    // versions.
  8391.     prefsSize = sizeof (AddrPreferenceType);
  8392.     prefsVersion = PrefGetAppPreferences (sysFileCAddress, addrPrefID, &prefs, &prefsSize, true);
  8393.     if (prefsVersion > addrPrefVersionNum) {
  8394.         prefsVersion = noPreferenceFound;
  8395.     }
  8396.     if (prefsVersion > noPreferenceFound)
  8397.         {
  8398.         if (prefsVersion < addrPrefVersionNum) {
  8399.             prefs.noteFont = prefs.v20NoteFont;
  8400.         }
  8401.         SaveBackup = prefs.saveBackup;
  8402.         RememberLastCategory = prefs.rememberLastCategory;
  8403.         if (prefs.noteFont == largeFont)
  8404.             NoteFont = largeBoldFont;
  8405.         else
  8406.             NoteFont = prefs.noteFont;
  8407.  
  8408.         // If the preferences are set to use the last category and if the
  8409.         // category hasn't been deleted then use the last category.
  8410.         if (RememberLastCategory &&
  8411.             prefs.currentCategory != dmAllCategories &&
  8412.             appInfoPtr->categoryLabels[prefs.currentCategory][0] != '\0')
  8413.             {
  8414.             CurrentCategory = prefs.currentCategory;
  8415.             ShowAllCategories = prefs.showAllCategories;
  8416.             }
  8417.         
  8418.         // Support transferal of preferences from the previous version of the software.
  8419.         if (prefsVersion == addrPrefVersionNum)
  8420.             {
  8421.             // Values not set here are left at their default values
  8422.             AddrListFont = prefs.addrListFont;
  8423.             AddrRecordFont = prefs.addrRecordFont;
  8424.             AddrEditFont = prefs.addrEditFont;
  8425.             BusinessCardRecordID = prefs.businessCardRecordID;
  8426.             }
  8427.         }
  8428.     
  8429.     // The first time this app starts register to handle vCard data.
  8430.     if (prefsVersion != addrPrefVersionNum)
  8431.         ExgRegisterData(sysFileCAddress, exgRegExtensionID, "vcf");
  8432.    
  8433.  
  8434.     MemPtrUnlock(appInfoPtr);
  8435. }
  8436.  
  8437. /***********************************************************************
  8438.  *
  8439.  * FUNCTION:    AddressSavePrefs
  8440.  *
  8441.  * DESCRIPTION: Save the Address preferences with fixups so that
  8442.  *                previous versions won't go crazy.
  8443.  *
  8444.  * PARAMETERS:  nothing
  8445.  *
  8446.  * RETURNED:    nothing
  8447.  *
  8448.  * REVISION HISTORY:
  8449.  *         Name   Date      Description
  8450.  *         ----   ----      -----------
  8451.  *         BGT   1/8/98     Initial Revision
  8452.  *
  8453.  ***********************************************************************/
  8454.  
  8455. void AddressSavePrefs(void)
  8456. {
  8457.     AddrPreferenceType prefs;
  8458.  
  8459.  
  8460.     // Write the preferences / saved-state information.
  8461.     prefs.currentCategory = CurrentCategory;
  8462.     ErrNonFatalDisplayIf(NoteFont > largeBoldFont, "Note font invalid.");
  8463.     prefs.noteFont = NoteFont;
  8464.     if (prefs.noteFont > largeFont) {
  8465.         prefs.v20NoteFont = stdFont;
  8466.     }
  8467.     else {
  8468.         prefs.v20NoteFont = prefs.noteFont;
  8469.     }
  8470.     prefs.addrListFont = AddrListFont;
  8471.     prefs.addrRecordFont = AddrRecordFont;
  8472.     prefs.addrEditFont = AddrEditFont;
  8473.     prefs.showAllCategories = ShowAllCategories;
  8474.     prefs.saveBackup = SaveBackup;
  8475.     prefs.rememberLastCategory = RememberLastCategory;
  8476.     prefs.businessCardRecordID = BusinessCardRecordID;
  8477.        
  8478.     // Write the state information.
  8479.     PrefSetAppPreferences (sysFileCAddress, addrPrefID, addrPrefVersionNum, &prefs, 
  8480.         sizeof (AddrPreferenceType), true);
  8481. }
  8482.  
  8483.  
  8484. /***********************************************************************
  8485.  *
  8486.  * FUNCTION:    RomVersionCompatible
  8487.  *
  8488.  * DESCRIPTION: This routine checks that a ROM version meets your
  8489.  *              minimum requirement.
  8490.  *
  8491.  * PARAMETERS:  requiredVersion - minimum rom version required
  8492.  *                                (see sysFtrNumROMVersion in SystemMgr.h 
  8493.  *                                for format)
  8494.  *              launchFlags     - flags that indicate if the application 
  8495.  *                                UI is initialized.
  8496.  *
  8497.  * RETURNED:    error code or zero if rom is compatible
  8498.  *                             
  8499.  *
  8500.  * REVISION HISTORY:
  8501.  *            Name    Date        Description
  8502.  *            ----    ----        -----------
  8503.  *            art    11/15/96    Initial Revision
  8504.  *
  8505.  ***********************************************************************/
  8506. static Err RomVersionCompatible (DWord requiredVersion, Word launchFlags)
  8507. {
  8508.     DWord romVersion;
  8509.  
  8510.     // See if we have at least the minimum required version of the ROM or later.
  8511.     FtrGet(sysFtrCreator, sysFtrNumROMVersion, &romVersion);
  8512.     if (romVersion < requiredVersion)
  8513.         {
  8514.         if ((launchFlags & (sysAppLaunchFlagNewGlobals | sysAppLaunchFlagUIApp)) ==
  8515.             (sysAppLaunchFlagNewGlobals | sysAppLaunchFlagUIApp))
  8516.             {
  8517.             FrmAlert (RomIncompatibleAlert);
  8518.         
  8519.             // Pilot 1.0 will continuously relaunch this app unless we switch to 
  8520.             // another safe one.
  8521.             if (romVersion < 0x02000000)
  8522.                 AppLaunchWithCommand(sysFileCDefaultApp, sysAppLaunchCmdNormalLaunch, NULL);
  8523.             }
  8524.         
  8525.         return (sysErrRomIncompatible);
  8526.         }
  8527.  
  8528.     return (0);
  8529. }
  8530.  
  8531.  
  8532.